]> git.openstreetmap.org Git - rails.git/blob - vendor/assets/iD/iD.js
Merge remote-tracking branch 'upstream/pull/2725'
[rails.git] / vendor / assets / iD / iD.js
1 (function () {
2         var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
3
4         function createCommonjsModule(fn, basedir, module) {
5                 return module = {
6                   path: basedir,
7                   exports: {},
8                   require: function (path, base) {
9               return commonjsRequire(path, (base === undefined || base === null) ? module.path : base);
10             }
11                 }, fn(module, module.exports), module.exports;
12         }
13
14         function getCjsExportFromNamespace (n) {
15                 return n && n['default'] || n;
16         }
17
18         function commonjsRequire () {
19                 throw new Error('Dynamic requires are not currently supported by @rollup/plugin-commonjs');
20         }
21
22         var isImplemented = function () {
23                 var set, iterator, result;
24                 if (typeof Set !== 'function') { return false; }
25                 set = new Set(['raz', 'dwa', 'trzy']);
26                 if (String(set) !== '[object Set]') { return false; }
27                 if (set.size !== 3) { return false; }
28                 if (typeof set.add !== 'function') { return false; }
29                 if (typeof set.clear !== 'function') { return false; }
30                 if (typeof set.delete !== 'function') { return false; }
31                 if (typeof set.entries !== 'function') { return false; }
32                 if (typeof set.forEach !== 'function') { return false; }
33                 if (typeof set.has !== 'function') { return false; }
34                 if (typeof set.keys !== 'function') { return false; }
35                 if (typeof set.values !== 'function') { return false; }
36
37                 iterator = set.values();
38                 result = iterator.next();
39                 if (result.done !== false) { return false; }
40                 if (result.value !== 'raz') { return false; }
41
42                 return true;
43         };
44
45         // eslint-disable-next-line no-empty-function
46         var noop = function () {};
47
48         var _undefined = noop(); // Support ES3 engines
49
50         var isValue = function (val) { return val !== _undefined && val !== null; };
51
52         var validValue = function (value) {
53                 if (!isValue(value)) { throw new TypeError("Cannot use null or undefined"); }
54                 return value;
55         };
56
57         var clear = function () {
58                 validValue(this).length = 0;
59                 return this;
60         };
61
62         var isImplemented$1 = function () {
63                 var numberIsNaN = Number.isNaN;
64                 if (typeof numberIsNaN !== "function") { return false; }
65                 return !numberIsNaN({}) && numberIsNaN(NaN) && !numberIsNaN(34);
66         };
67
68         var shim = function (value) {
69                 // eslint-disable-next-line no-self-compare
70                 return value !== value;
71         };
72
73         var isNan = isImplemented$1() ? Number.isNaN : shim;
74
75         var isImplemented$2 = function () {
76                 var sign = Math.sign;
77                 if (typeof sign !== "function") { return false; }
78                 return sign(10) === 1 && sign(-20) === -1;
79         };
80
81         var shim$1 = function (value) {
82                 value = Number(value);
83                 if (isNaN(value) || value === 0) { return value; }
84                 return value > 0 ? 1 : -1;
85         };
86
87         var sign = isImplemented$2() ? Math.sign : shim$1;
88
89         var abs   = Math.abs
90           , floor = Math.floor;
91
92         var toInteger = function (value) {
93                 if (isNaN(value)) { return 0; }
94                 value = Number(value);
95                 if (value === 0 || !isFinite(value)) { return value; }
96                 return sign(value) * floor(abs(value));
97         };
98
99         var max       = Math.max;
100
101         var toPosInteger = function (value) { return max(0, toInteger(value)); };
102
103         var indexOf           = Array.prototype.indexOf
104           , objHasOwnProperty = Object.prototype.hasOwnProperty
105           , abs$1               = Math.abs
106           , floor$1             = Math.floor;
107
108         var eIndexOf = function (searchElement/*, fromIndex*/) {
109                 var i, length, fromIndex, val;
110                 if (!isNan(searchElement)) { return indexOf.apply(this, arguments); }
111
112                 length = toPosInteger(validValue(this).length);
113                 fromIndex = arguments[1];
114                 if (isNaN(fromIndex)) { fromIndex = 0; }
115                 else if (fromIndex >= 0) { fromIndex = floor$1(fromIndex); }
116                 else { fromIndex = toPosInteger(this.length) - floor$1(abs$1(fromIndex)); }
117
118                 for (i = fromIndex; i < length; ++i) {
119                         if (objHasOwnProperty.call(this, i)) {
120                                 val = this[i];
121                                 if (isNan(val)) { return i; } // Jslint: ignore
122                         }
123                 }
124                 return -1;
125         };
126
127         var create = Object.create, getPrototypeOf = Object.getPrototypeOf, plainObject = {};
128
129         var isImplemented$3 = function (/* CustomCreate*/) {
130                 var setPrototypeOf = Object.setPrototypeOf, customCreate = arguments[0] || create;
131                 if (typeof setPrototypeOf !== "function") { return false; }
132                 return getPrototypeOf(setPrototypeOf(customCreate(null), plainObject)) === plainObject;
133         };
134
135         var map = { function: true, object: true };
136
137         var isObject = function (value) { return (isValue(value) && map[typeof value]) || false; };
138
139         var create$1 = Object.create, shim$2;
140
141         if (!isImplemented$3()) {
142                 shim$2 = shim$3;
143         }
144
145         var create_1 = (function () {
146                 var nullObject, polyProps, desc;
147                 if (!shim$2) { return create$1; }
148                 if (shim$2.level !== 1) { return create$1; }
149
150                 nullObject = {};
151                 polyProps = {};
152                 desc = { configurable: false, enumerable: false, writable: true, value: undefined };
153                 Object.getOwnPropertyNames(Object.prototype).forEach(function (name) {
154                         if (name === "__proto__") {
155                                 polyProps[name] = {
156                                         configurable: true,
157                                         enumerable: false,
158                                         writable: true,
159                                         value: undefined
160                                 };
161                                 return;
162                         }
163                         polyProps[name] = desc;
164                 });
165                 Object.defineProperties(nullObject, polyProps);
166
167                 Object.defineProperty(shim$2, "nullPolyfill", {
168                         configurable: false,
169                         enumerable: false,
170                         writable: false,
171                         value: nullObject
172                 });
173
174                 return function (prototype, props) {
175                         return create$1(prototype === null ? nullObject : prototype, props);
176                 };
177         })();
178
179         var objIsPrototypeOf = Object.prototype.isPrototypeOf
180           , defineProperty   = Object.defineProperty
181           , nullDesc         = { configurable: true, enumerable: false, writable: true, value: undefined }
182           , validate;
183
184         validate = function (obj, prototype) {
185                 validValue(obj);
186                 if (prototype === null || isObject(prototype)) { return obj; }
187                 throw new TypeError("Prototype must be null or an object");
188         };
189
190         var shim$3 = (function (status) {
191                 var fn, set;
192                 if (!status) { return null; }
193                 if (status.level === 2) {
194                         if (status.set) {
195                                 set = status.set;
196                                 fn = function (obj, prototype) {
197                                         set.call(validate(obj, prototype), prototype);
198                                         return obj;
199                                 };
200                         } else {
201                                 fn = function (obj, prototype) {
202                                         validate(obj, prototype).__proto__ = prototype;
203                                         return obj;
204                                 };
205                         }
206                 } else {
207                         fn = function self(obj, prototype) {
208                                 var isNullBase;
209                                 validate(obj, prototype);
210                                 isNullBase = objIsPrototypeOf.call(self.nullPolyfill, obj);
211                                 if (isNullBase) { delete self.nullPolyfill.__proto__; }
212                                 if (prototype === null) { prototype = self.nullPolyfill; }
213                                 obj.__proto__ = prototype;
214                                 if (isNullBase) { defineProperty(self.nullPolyfill, "__proto__", nullDesc); }
215                                 return obj;
216                         };
217                 }
218                 return Object.defineProperty(fn, "level", {
219                         configurable: false,
220                         enumerable: false,
221                         writable: false,
222                         value: status.level
223                 });
224         })(
225                 (function () {
226                         var tmpObj1 = Object.create(null)
227                           , tmpObj2 = {}
228                           , set
229                           , desc = Object.getOwnPropertyDescriptor(Object.prototype, "__proto__");
230
231                         if (desc) {
232                                 try {
233                                         set = desc.set; // Opera crashes at this point
234                                         set.call(tmpObj1, tmpObj2);
235                                 } catch (ignore) {}
236                                 if (Object.getPrototypeOf(tmpObj1) === tmpObj2) { return { set: set, level: 2 }; }
237                         }
238
239                         tmpObj1.__proto__ = tmpObj2;
240                         if (Object.getPrototypeOf(tmpObj1) === tmpObj2) { return { level: 2 }; }
241
242                         tmpObj1 = {};
243                         tmpObj1.__proto__ = tmpObj2;
244                         if (Object.getPrototypeOf(tmpObj1) === tmpObj2) { return { level: 1 }; }
245
246                         return false;
247                 })()
248         );
249
250         var setPrototypeOf = isImplemented$3() ? Object.setPrototypeOf : shim$3;
251
252         var validCallable = function (fn) {
253                 if (typeof fn !== "function") { throw new TypeError(fn + " is not a function"); }
254                 return fn;
255         };
256
257         // ES3 safe
258         var _undefined$1 = void 0;
259
260         var is = function (value) { return value !== _undefined$1 && value !== null; };
261
262         // prettier-ignore
263         var possibleTypes = { "object": true, "function": true, "undefined": true /* document.all */ };
264
265         var is$1 = function (value) {
266                 if (!is(value)) { return false; }
267                 return hasOwnProperty.call(possibleTypes, typeof value);
268         };
269
270         var is$2 = function (value) {
271                 if (!is$1(value)) { return false; }
272                 try {
273                         if (!value.constructor) { return false; }
274                         return value.constructor.prototype === value;
275                 } catch (error) {
276                         return false;
277                 }
278         };
279
280         var is$3 = function (value) {
281                 if (typeof value !== "function") { return false; }
282
283                 if (!hasOwnProperty.call(value, "length")) { return false; }
284
285                 try {
286                         if (typeof value.length !== "number") { return false; }
287                         if (typeof value.call !== "function") { return false; }
288                         if (typeof value.apply !== "function") { return false; }
289                 } catch (error) {
290                         return false;
291                 }
292
293                 return !is$2(value);
294         };
295
296         var classRe = /^\s*class[\s{/}]/, functionToString = Function.prototype.toString;
297
298         var is$4 = function (value) {
299                 if (!is$3(value)) { return false; }
300                 if (classRe.test(functionToString.call(value))) { return false; }
301                 return true;
302         };
303
304         var isImplemented$4 = function () {
305                 var assign = Object.assign, obj;
306                 if (typeof assign !== "function") { return false; }
307                 obj = { foo: "raz" };
308                 assign(obj, { bar: "dwa" }, { trzy: "trzy" });
309                 return obj.foo + obj.bar + obj.trzy === "razdwatrzy";
310         };
311
312         var isImplemented$5 = function () {
313                 try {
314                         Object.keys("primitive");
315                         return true;
316                 } catch (e) {
317                         return false;
318                 }
319         };
320
321         var keys = Object.keys;
322
323         var shim$4 = function (object) { return keys(isValue(object) ? Object(object) : object); };
324
325         var keys$1 = isImplemented$5() ? Object.keys : shim$4;
326
327         var max$1   = Math.max;
328
329         var shim$5 = function (dest, src/*, …srcn*/) {
330                 var 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 langauges
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 langauge
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 langauge this is based on
18525
18526                     if (_languageNames[base]) {   // base language name in locale langauge
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 hypen (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 (geom) {
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 (geom === 'relation' && key === 'type') {
18896               if (value in _this.tags) {
18897                 key = value;
18898                 value = _this.tags[key];
18899               } else {
18900                 return { rtype: value };
18901               }
18902             }
18903
18904             if (value === '*') {
18905               return { key: key };
18906             } else {
18907               return { key: key, value: value };
18908             }
18909           };
18910
18911
18912           _this.unsetTags = function (tags, geometry, skipFieldDefaults) {
18913             tags = utilObjectOmit(tags, Object.keys(_this.removeTags));
18914
18915             if (geometry && !skipFieldDefaults) {
18916               _this.fields().forEach(function (field) {
18917                 if (field.matchGeometry(geometry) && field.key && field.default === tags[field.key]) {
18918                   delete tags[field.key];
18919                 }
18920               });
18921             }
18922
18923             delete tags.area;
18924             return tags;
18925           };
18926
18927
18928           _this.setTags = function (tags, geometry, skipFieldDefaults) {
18929             var addTags = _this.addTags;
18930             tags = Object.assign({}, tags);   // shallow copy
18931
18932             for (var k in addTags) {
18933               if (addTags[k] === '*') {
18934                 tags[k] = 'yes';
18935               } else {
18936                 tags[k] = addTags[k];
18937               }
18938             }
18939
18940             // Add area=yes if necessary.
18941             // This is necessary if the geometry is already an area (e.g. user drew an area) AND any of:
18942             // 1. chosen preset could be either an area or a line (`barrier=city_wall`)
18943             // 2. chosen preset doesn't have a key in osmAreaKeys (`railway=station`)
18944             if (!addTags.hasOwnProperty('area')) {
18945               delete tags.area;
18946               if (geometry === 'area') {
18947                 var needsAreaTag = true;
18948                 if (_this.geometry.indexOf('line') === -1) {
18949                   for (var k$1 in addTags) {
18950                     if (k$1 in osmAreaKeys) {
18951                       needsAreaTag = false;
18952                       break;
18953                     }
18954                   }
18955                 }
18956                 if (needsAreaTag) {
18957                   tags.area = 'yes';
18958                 }
18959               }
18960             }
18961
18962             if (geometry && !skipFieldDefaults) {
18963               _this.fields().forEach(function (field) {
18964                 if (field.matchGeometry(geometry) && field.key && !tags[field.key] && field.default) {
18965                   tags[field.key] = field.default;
18966                 }
18967               });
18968             }
18969
18970             return tags;
18971           };
18972
18973
18974           // For a preset without fields, use the fields of the parent preset.
18975           // Replace {preset} placeholders with the fields of the specified presets.
18976           function resolve(which) {
18977             var fieldIDs = (which === 'fields' ? _this.originalFields : _this.originalMoreFields);
18978             var resolved = [];
18979
18980             fieldIDs.forEach(function (fieldID) {
18981               var match = fieldID.match(/\{(.*)\}/);
18982               if (match !== null) {    // a presetID wrapped in braces {}
18983                 resolved = resolved.concat(inheritFields(match[1], which));
18984               } else if (allFields[fieldID]) {    // a normal fieldID
18985                 resolved.push(allFields[fieldID]);
18986               } else {
18987                 console.log(("Cannot resolve \"" + fieldID + "\" found in " + (_this.id) + "." + which));  // eslint-disable-line no-console
18988               }
18989             });
18990
18991             // no fields resolved, so use the parent's if possible
18992             if (!resolved.length) {
18993               var endIndex = _this.id.lastIndexOf('/');
18994               var parentID = endIndex && _this.id.substring(0, endIndex);
18995               if (parentID) {
18996                 resolved = inheritFields(parentID, which);
18997               }
18998             }
18999
19000             return utilArrayUniq(resolved);
19001
19002
19003             // returns an array of fields to inherit from the given presetID, if found
19004             function inheritFields(presetID, which) {
19005               var parent = allPresets[presetID];
19006               if (!parent) { return []; }
19007
19008               if (which === 'fields') {
19009                 return parent.fields().filter(shouldInherit);
19010               } else if (which === 'moreFields') {
19011                 return parent.moreFields();
19012               } else {
19013                 return [];
19014               }
19015             }
19016
19017
19018             // Skip `fields` for the keys which define the preset.
19019             // These are usually `typeCombo` fields like `shop=*`
19020             function shouldInherit(f) {
19021               if (f.key && _this.tags[f.key] !== undefined &&
19022                 // inherit anyway if multiple values are allowed or just a checkbox
19023                 f.type !== 'multiCombo' && f.type !== 'semiCombo' && f.type !== 'check'
19024               ) { return false; }
19025
19026               return true;
19027             }
19028           }
19029
19030
19031           return _this;
19032         }
19033
19034         var _mainPresetIndex = presetIndex(); // singleton
19035
19036         //
19037         // `presetIndex` wraps a `presetCollection`
19038         // with methods for loading new data and returning defaults
19039         //
19040         function presetIndex() {
19041           var dispatch$1 = dispatch('favoritePreset', 'recentsChange');
19042           var MAXRECENTS = 30;
19043
19044           // seed the preset lists with geometry fallbacks
19045           var POINT = presetPreset('point', { name: 'Point', tags: {}, geometry: ['point', 'vertex'], matchScore: 0.1 } );
19046           var LINE = presetPreset('line', { name: 'Line', tags: {}, geometry: ['line'], matchScore: 0.1 } );
19047           var AREA = presetPreset('area', { name: 'Area', tags: { area: 'yes' }, geometry: ['area'], matchScore: 0.1 } );
19048           var RELATION = presetPreset('relation', { name: 'Relation', tags: {}, geometry: ['relation'], matchScore: 0.1 } );
19049
19050           var _this = presetCollection([POINT, LINE, AREA, RELATION]);
19051           var _presets = { point: POINT, line: LINE, area: AREA, relation: RELATION };
19052
19053           var _defaults = {
19054             point: presetCollection([POINT]),
19055             vertex: presetCollection([POINT]),
19056             line: presetCollection([LINE]),
19057             area: presetCollection([AREA]),
19058             relation: presetCollection([RELATION])
19059           };
19060
19061           var _fields = {};
19062           var _categories = {};
19063           var _universal = [];
19064           var _addablePresetIDs = null;   // Set of preset IDs that the user can add
19065           var _recents;
19066           var _favorites;
19067
19068           // Index of presets by (geometry, tag key).
19069           var _geometryIndex = { point: {}, vertex: {}, line: {}, area: {}, relation: {} };
19070
19071           var _loadPromise;
19072
19073           _this.ensureLoaded = function () {
19074             if (_loadPromise) { return _loadPromise; }
19075
19076             return _loadPromise = Promise.all([
19077                 _mainFileFetcher.get('preset_categories'),
19078                 _mainFileFetcher.get('preset_defaults'),
19079                 _mainFileFetcher.get('preset_presets'),
19080                 _mainFileFetcher.get('preset_fields')
19081               ])
19082               .then(function (vals) {
19083                 _this.merge({
19084                   categories: vals[0],
19085                   defaults: vals[1],
19086                   presets: vals[2],
19087                   fields: vals[3]
19088                 });
19089                 osmSetAreaKeys(_this.areaKeys());
19090                 osmSetPointTags(_this.pointTags());
19091                 osmSetVertexTags(_this.vertexTags());
19092               });
19093           };
19094
19095
19096           _this.merge = function (d) {
19097             // Merge Fields
19098             if (d.fields) {
19099               Object.keys(d.fields).forEach(function (fieldID) {
19100                 var f = d.fields[fieldID];
19101                 if (f) {   // add or replace
19102                   _fields[fieldID] = presetField(fieldID, f);
19103                 } else {   // remove
19104                   delete _fields[fieldID];
19105                 }
19106               });
19107             }
19108
19109             // Merge Presets
19110             if (d.presets) {
19111               Object.keys(d.presets).forEach(function (presetID) {
19112                 var p = d.presets[presetID];
19113                 if (p) {   // add or replace
19114                   var isAddable = !_addablePresetIDs || _addablePresetIDs.has(presetID);
19115                   _presets[presetID] = presetPreset(presetID, p, isAddable, _fields, _presets);
19116                 } else {   // remove (but not if it's a fallback)
19117                   var existing = _presets[presetID];
19118                   if (existing && !existing.isFallback()) {
19119                     delete _presets[presetID];
19120                   }
19121                 }
19122               });
19123             }
19124
19125             // Need to rebuild _this.collection before loading categories
19126             _this.collection = Object.values(_presets).concat(Object.values(_categories));
19127
19128             // Merge Categories
19129             if (d.categories) {
19130               Object.keys(d.categories).forEach(function (categoryID) {
19131                 var c = d.categories[categoryID];
19132                 if (c) {   // add or replace
19133                   _categories[categoryID] = presetCategory(categoryID, c, _this);
19134                 } else {   // remove
19135                   delete _categories[categoryID];
19136                 }
19137               });
19138             }
19139
19140             // Rebuild _this.collection after loading categories
19141             _this.collection = Object.values(_presets).concat(Object.values(_categories));
19142
19143             // Merge Defaults
19144             if (d.defaults) {
19145               Object.keys(d.defaults).forEach(function (geometry) {
19146                 var def = d.defaults[geometry];
19147                 if (Array.isArray(def)) {   // add or replace
19148                   _defaults[geometry] = presetCollection(
19149                     def.map(function (id) { return _presets[id] || _categories[id]; }).filter(Boolean)
19150                   );
19151                 } else {   // remove
19152                   delete _defaults[geometry];
19153                 }
19154               });
19155             }
19156
19157             // Rebuild universal fields array
19158             _universal = Object.values(_fields).filter(function (field) { return field.universal; });
19159
19160             // Reset all the preset fields - they'll need to be resolved again
19161             Object.values(_presets).forEach(function (preset) { return preset.resetFields(); });
19162
19163             // Rebuild geometry index
19164             _geometryIndex = { point: {}, vertex: {}, line: {}, area: {}, relation: {} };
19165             _this.collection.forEach(function (preset) {
19166               (preset.geometry || []).forEach(function (geometry) {
19167                 var g = _geometryIndex[geometry];
19168                 for (var key in preset.tags) {
19169                   (g[key] = g[key] || []).push(preset);
19170                 }
19171               });
19172             });
19173
19174             return _this;
19175           };
19176
19177
19178           _this.match = function (entity, resolver) {
19179             return resolver.transient(entity, 'presetMatch', function () {
19180               var geometry = entity.geometry(resolver);
19181               // Treat entities on addr:interpolation lines as points, not vertices - #3241
19182               if (geometry === 'vertex' && entity.isOnAddressLine(resolver)) {
19183                 geometry = 'point';
19184               }
19185               return _this.matchTags(entity.tags, geometry);
19186             });
19187           };
19188
19189
19190           _this.matchTags = function (tags, geometry) {
19191             var geometryMatches = _geometryIndex[geometry];
19192             var address;
19193             var best = -1;
19194             var match;
19195
19196             for (var k in tags) {
19197               // If any part of an address is present, allow fallback to "Address" preset - #4353
19198               if (/^addr:/.test(k) && geometryMatches['addr:*']) {
19199                 address = geometryMatches['addr:*'][0];
19200               }
19201
19202               var keyMatches = geometryMatches[k];
19203               if (!keyMatches) { continue; }
19204
19205               for (var i = 0; i < keyMatches.length; i++) {
19206                 var score = keyMatches[i].matchScore(tags);
19207                 if (score > best) {
19208                   best = score;
19209                   match = keyMatches[i];
19210                 }
19211               }
19212             }
19213
19214             if (address && (!match || match.isFallback())) {
19215               match = address;
19216             }
19217             return match || _this.fallback(geometry);
19218           };
19219
19220
19221           _this.allowsVertex = function (entity, resolver) {
19222             if (entity.type !== 'node') { return false; }
19223             if (Object.keys(entity.tags).length === 0) { return true; }
19224
19225             return resolver.transient(entity, 'vertexMatch', function () {
19226               // address lines allow vertices to act as standalone points
19227               if (entity.isOnAddressLine(resolver)) { return true; }
19228
19229               var geometries = osmNodeGeometriesForTags(entity.tags);
19230               if (geometries.vertex) { return true; }
19231               if (geometries.point) { return false; }
19232               // allow vertices for unspecified points
19233               return true;
19234             });
19235           };
19236
19237
19238           // Because of the open nature of tagging, iD will never have a complete
19239           // list of tags used in OSM, so we want it to have logic like "assume
19240           // that a closed way with an amenity tag is an area, unless the amenity
19241           // is one of these specific types". This function computes a structure
19242           // that allows testing of such conditions, based on the presets designated
19243           // as as supporting (or not supporting) the area geometry.
19244           //
19245           // The returned object L is a keeplist/discardlist of tags. A closed way
19246           // with a tag (k, v) is considered to be an area if `k in L && !(v in L[k])`
19247           // (see `Way#isArea()`). In other words, the keys of L form the keeplist,
19248           // and the subkeys form the discardlist.
19249           _this.areaKeys = function () {
19250             // The ignore list is for keys that imply lines. (We always add `area=yes` for exceptions)
19251             var ignore = ['barrier', 'highway', 'footway', 'railway', 'junction', 'type'];
19252             var areaKeys = {};
19253
19254             // ignore name-suggestion-index and deprecated presets
19255             var presets = _this.collection.filter(function (p) { return !p.suggestion && !p.replacement; });
19256
19257             // keeplist
19258             presets.forEach(function (p) {
19259               var key;
19260               for (key in p.tags) { break; }  // pick the first tag
19261               if (!key) { return; }
19262               if (ignore.indexOf(key) !== -1) { return; }
19263
19264               if (p.geometry.indexOf('area') !== -1) {    // probably an area..
19265                 areaKeys[key] = areaKeys[key] || {};
19266               }
19267             });
19268
19269             // discardlist
19270             presets.forEach(function (p) {
19271               var key;
19272               for (key in p.addTags) {
19273                 // examine all addTags to get a better sense of what can be tagged on lines - #6800
19274                 var value = p.addTags[key];
19275                 if (key in areaKeys &&                    // probably an area...
19276                   p.geometry.indexOf('line') !== -1 &&    // but sometimes a line
19277                   value !== '*') {
19278                   areaKeys[key][value] = true;
19279                 }
19280               }
19281             });
19282
19283             return areaKeys;
19284           };
19285
19286
19287           _this.pointTags = function () {
19288             return _this.collection.reduce(function (pointTags, d) {
19289               // ignore name-suggestion-index, deprecated, and generic presets
19290               if (d.suggestion || d.replacement || d.searchable === false) { return pointTags; }
19291
19292               // only care about the primary tag
19293               var key;
19294               for (key in d.tags) { break; }  // pick the first tag
19295               if (!key) { return pointTags; }
19296
19297               // if this can be a point
19298               if (d.geometry.indexOf('point') !== -1) {
19299                 pointTags[key] = pointTags[key] || {};
19300                 pointTags[key][d.tags[key]] = true;
19301               }
19302               return pointTags;
19303             }, {});
19304           };
19305
19306
19307           _this.vertexTags = function () {
19308             return _this.collection.reduce(function (vertexTags, d) {
19309               // ignore name-suggestion-index, deprecated, and generic presets
19310               if (d.suggestion || d.replacement || d.searchable === false) { return vertexTags; }
19311
19312               // only care about the primary tag
19313               var key;
19314               for (key in d.tags) { break; }   // pick the first tag
19315               if (!key) { return vertexTags; }
19316
19317               // if this can be a vertex
19318               if (d.geometry.indexOf('vertex') !== -1) {
19319                 vertexTags[key] = vertexTags[key] || {};
19320                 vertexTags[key][d.tags[key]] = true;
19321               }
19322               return vertexTags;
19323             }, {});
19324           };
19325
19326
19327           _this.field = function (id) { return _fields[id]; };
19328
19329           _this.universal = function () { return _universal; };
19330
19331
19332           _this.defaults = function (geometry, n, startWithRecents) {
19333             var recents = [];
19334             if (startWithRecents) {
19335               recents = _this.recent().matchGeometry(geometry).collection.slice(0, 4);
19336             }
19337             var defaults;
19338             if (_addablePresetIDs) {
19339               defaults = Array.from(_addablePresetIDs).map(function(id) {
19340                 var preset = _this.item(id);
19341                 if (preset && preset.matchGeometry(geometry)) { return preset; }
19342                 return null;
19343               }).filter(Boolean);
19344             } else {
19345               defaults = _defaults[geometry].collection.concat(_this.fallback(geometry));
19346             }
19347
19348             return presetCollection(
19349               utilArrayUniq(recents.concat(defaults)).slice(0, n - 1)
19350             );
19351           };
19352
19353           // pass a Set of addable preset ids
19354           _this.addablePresetIDs = function(val) {
19355             if (!arguments.length) { return _addablePresetIDs; }
19356
19357             // accept and convert arrays
19358             if (Array.isArray(val)) { val = new Set(val); }
19359
19360             _addablePresetIDs = val;
19361             if (_addablePresetIDs) {   // reset all presets
19362               _this.collection.forEach(function (p) {
19363                 // categories aren't addable
19364                 if (p.addable) { p.addable(_addablePresetIDs.has(p.id)); }
19365               });
19366             } else {
19367               _this.collection.forEach(function (p) {
19368                 if (p.addable) { p.addable(true); }
19369               });
19370             }
19371
19372             return _this;
19373           };
19374
19375
19376           _this.recent = function () {
19377             return presetCollection(
19378               utilArrayUniq(_this.getRecents().map(function (d) { return d.preset; }))
19379             );
19380           };
19381
19382
19383           function RibbonItem(preset, source) {
19384             var item = {};
19385             item.preset = preset;
19386             item.source = source;
19387
19388             item.isFavorite = function () { return item.source === 'favorite'; };
19389             item.isRecent = function () { return item.source === 'recent'; };
19390             item.matches = function (preset) { return item.preset.id === preset.id; };
19391             item.minified = function () { return ({ pID: item.preset.id }); };
19392
19393             return item;
19394           }
19395
19396
19397           function ribbonItemForMinified(d, source) {
19398             if (d && d.pID) {
19399               var preset = _this.item(d.pID);
19400               if (!preset) { return null; }
19401               return RibbonItem(preset, source);
19402             }
19403             return null;
19404           }
19405
19406
19407           _this.getGenericRibbonItems = function () {
19408             return ['point', 'line', 'area'].map(function (id) { return RibbonItem(_this.item(id), 'generic'); });
19409           };
19410
19411
19412           _this.getAddable = function () {
19413               if (!_addablePresetIDs) { return []; }
19414
19415               return _addablePresetIDs.map(function (id) {
19416                 var preset = _this.item(id);
19417                 if (preset) {
19418                   return RibbonItem(preset, 'addable');
19419                 }
19420               }).filter(Boolean);
19421           };
19422
19423
19424           function setRecents(items) {
19425             _recents = items;
19426             var minifiedItems = items.map(function (d) { return d.minified(); });
19427             corePreferences('preset_recents', JSON.stringify(minifiedItems));
19428             dispatch$1.call('recentsChange');
19429           }
19430
19431
19432           _this.getRecents = function () {
19433             if (!_recents) {
19434               // fetch from local storage
19435               _recents = (JSON.parse(corePreferences('preset_recents')) || [])
19436                 .reduce(function (acc, d) {
19437                   var item = ribbonItemForMinified(d, 'recent');
19438                   if (item && item.preset.addable()) { acc.push(item); }
19439                   return acc;
19440                 }, []);
19441             }
19442             return _recents;
19443           };
19444
19445
19446           _this.addRecent = function (preset, besidePreset, after) {
19447             var recents = _this.getRecents();
19448
19449             var beforeItem = _this.recentMatching(besidePreset);
19450             var toIndex = recents.indexOf(beforeItem);
19451             if (after) { toIndex += 1; }
19452
19453             var newItem = RibbonItem(preset, 'recent');
19454             recents.splice(toIndex, 0, newItem);
19455             setRecents(recents);
19456           };
19457
19458
19459           _this.removeRecent = function (preset) {
19460             var item = _this.recentMatching(preset);
19461             if (item) {
19462               var items = _this.getRecents();
19463               items.splice(items.indexOf(item), 1);
19464               setRecents(items);
19465             }
19466           };
19467
19468
19469           _this.recentMatching = function (preset) {
19470             var items = _this.getRecents();
19471             for (var i in items) {
19472               if (items[i].matches(preset)) {
19473                 return items[i];
19474               }
19475             }
19476             return null;
19477           };
19478
19479
19480           _this.moveItem = function (items, fromIndex, toIndex) {
19481             if (fromIndex === toIndex ||
19482               fromIndex < 0 || toIndex < 0 ||
19483               fromIndex >= items.length || toIndex >= items.length
19484             ) { return null; }
19485
19486             items.splice(toIndex, 0, items.splice(fromIndex, 1)[0]);
19487             return items;
19488           };
19489
19490
19491           _this.moveRecent = function (item, beforeItem) {
19492             var recents = _this.getRecents();
19493             var fromIndex = recents.indexOf(item);
19494             var toIndex = recents.indexOf(beforeItem);
19495             var items = _this.moveItem(recents, fromIndex, toIndex);
19496             if (items) { setRecents(items); }
19497           };
19498
19499
19500           _this.setMostRecent = function (preset) {
19501             if (preset.searchable === false) { return; }
19502
19503             var items = _this.getRecents();
19504             var item = _this.recentMatching(preset);
19505             if (item) {
19506               items.splice(items.indexOf(item), 1);
19507             } else {
19508               item = RibbonItem(preset, 'recent');
19509             }
19510
19511             // remove the last recent (first in, first out)
19512             while (items.length >= MAXRECENTS) {
19513               items.pop();
19514             }
19515
19516             // prepend array
19517             items.unshift(item);
19518             setRecents(items);
19519           };
19520
19521           function setFavorites(items) {
19522             _favorites = items;
19523             var minifiedItems = items.map(function (d) { return d.minified(); });
19524             corePreferences('preset_favorites', JSON.stringify(minifiedItems));
19525
19526             // call update
19527             dispatch$1.call('favoritePreset');
19528           }
19529
19530           _this.addFavorite = function (preset, besidePreset, after) {
19531               var favorites = _this.getFavorites();
19532
19533               var beforeItem = _this.favoriteMatching(besidePreset);
19534               var toIndex = favorites.indexOf(beforeItem);
19535               if (after) { toIndex += 1; }
19536
19537               var newItem = RibbonItem(preset, 'favorite');
19538               favorites.splice(toIndex, 0, newItem);
19539               setFavorites(favorites);
19540           };
19541
19542           _this.toggleFavorite = function (preset) {
19543             var favs = _this.getFavorites();
19544             var favorite = _this.favoriteMatching(preset);
19545             if (favorite) {
19546               favs.splice(favs.indexOf(favorite), 1);
19547             } else {
19548               // only allow 10 favorites
19549               if (favs.length === 10) {
19550                   // remove the last favorite (last in, first out)
19551                   favs.pop();
19552               }
19553               // append array
19554               favs.push(RibbonItem(preset, 'favorite'));
19555             }
19556             setFavorites(favs);
19557           };
19558
19559
19560           _this.removeFavorite = function (preset) {
19561             var item = _this.favoriteMatching(preset);
19562             if (item) {
19563               var items = _this.getFavorites();
19564               items.splice(items.indexOf(item), 1);
19565               setFavorites(items);
19566             }
19567           };
19568
19569
19570           _this.getFavorites = function () {
19571             if (!_favorites) {
19572
19573               // fetch from local storage
19574               var rawFavorites = JSON.parse(corePreferences('preset_favorites'));
19575
19576               if (!rawFavorites) {
19577                 rawFavorites = [];
19578                 corePreferences('preset_favorites', JSON.stringify(rawFavorites));
19579               }
19580
19581               _favorites = rawFavorites.reduce(function (output, d) {
19582                 var item = ribbonItemForMinified(d, 'favorite');
19583                 if (item && item.preset.addable()) { output.push(item); }
19584                 return output;
19585               }, []);
19586             }
19587             return _favorites;
19588           };
19589
19590
19591           _this.favoriteMatching = function (preset) {
19592             var favs = _this.getFavorites();
19593             for (var index in favs) {
19594               if (favs[index].matches(preset)) {
19595                 return favs[index];
19596               }
19597             }
19598             return null;
19599           };
19600
19601
19602           return utilRebind(_this, dispatch$1, 'on');
19603         }
19604
19605         function utilTagText(entity) {
19606             var obj = (entity && entity.tags) || {};
19607             return Object.keys(obj)
19608                 .map(function(k) { return k + '=' + obj[k]; })
19609                 .join(', ');
19610         }
19611
19612
19613         function utilTotalExtent(array, graph) {
19614             var extent = geoExtent();
19615             var val, entity;
19616             for (var i = 0; i < array.length; i++) {
19617                 val = array[i];
19618                 entity = typeof val === 'string' ? graph.hasEntity(val) : val;
19619                 if (entity) {
19620                     extent._extend(entity.extent(graph));
19621                 }
19622             }
19623             return extent;
19624         }
19625
19626
19627         function utilTagDiff(oldTags, newTags) {
19628             var tagDiff = [];
19629             var keys = utilArrayUnion(Object.keys(oldTags), Object.keys(newTags)).sort();
19630             keys.forEach(function(k) {
19631                 var oldVal = oldTags[k];
19632                 var newVal = newTags[k];
19633
19634                 if ((oldVal || oldVal === '') && (newVal === undefined || newVal !== oldVal)) {
19635                     tagDiff.push({
19636                         type: '-',
19637                         key: k,
19638                         oldVal: oldVal,
19639                         newVal: newVal,
19640                         display: '- ' + k + '=' + oldVal
19641                     });
19642                 }
19643                 if ((newVal || newVal === '') && (oldVal === undefined || newVal !== oldVal)) {
19644                     tagDiff.push({
19645                         type: '+',
19646                         key: k,
19647                         oldVal: oldVal,
19648                         newVal: newVal,
19649                         display: '+ ' + k + '=' + newVal
19650                     });
19651                 }
19652             });
19653             return tagDiff;
19654         }
19655
19656
19657         function utilEntitySelector(ids) {
19658             return ids.length ? '.' + ids.join(',.') : 'nothing';
19659         }
19660
19661
19662         // returns an selector to select entity ids for:
19663         //  - entityIDs passed in
19664         //  - shallow descendant entityIDs for any of those entities that are relations
19665         function utilEntityOrMemberSelector(ids, graph) {
19666             var seen = new Set(ids);
19667             ids.forEach(collectShallowDescendants);
19668             return utilEntitySelector(Array.from(seen));
19669
19670             function collectShallowDescendants(id) {
19671                 var entity = graph.hasEntity(id);
19672                 if (!entity || entity.type !== 'relation') { return; }
19673
19674                 entity.members
19675                     .map(function(member) { return member.id; })
19676                     .forEach(function(id) { seen.add(id); });
19677             }
19678         }
19679
19680
19681         // returns an selector to select entity ids for:
19682         //  - entityIDs passed in
19683         //  - deep descendant entityIDs for any of those entities that are relations
19684         function utilEntityOrDeepMemberSelector(ids, graph) {
19685             return utilEntitySelector(utilEntityAndDeepMemberIDs(ids, graph));
19686         }
19687
19688
19689         // returns an selector to select entity ids for:
19690         //  - entityIDs passed in
19691         //  - deep descendant entityIDs for any of those entities that are relations
19692         function utilEntityAndDeepMemberIDs(ids, graph) {
19693             var seen = new Set();
19694             ids.forEach(collectDeepDescendants);
19695             return Array.from(seen);
19696
19697             function collectDeepDescendants(id) {
19698                 if (seen.has(id)) { return; }
19699                 seen.add(id);
19700
19701                 var entity = graph.hasEntity(id);
19702                 if (!entity || entity.type !== 'relation') { return; }
19703
19704                 entity.members
19705                     .map(function(member) { return member.id; })
19706                     .forEach(collectDeepDescendants);   // recurse
19707             }
19708         }
19709
19710         // returns an selector to select entity ids for:
19711         //  - deep descendant entityIDs for any of those entities that are relations
19712         function utilDeepMemberSelector(ids, graph, skipMultipolgonMembers) {
19713             var idsSet = new Set(ids);
19714             var seen = new Set();
19715             var returners = new Set();
19716             ids.forEach(collectDeepDescendants);
19717             return utilEntitySelector(Array.from(returners));
19718
19719             function collectDeepDescendants(id) {
19720                 if (seen.has(id)) { return; }
19721                 seen.add(id);
19722
19723                 if (!idsSet.has(id)) {
19724                     returners.add(id);
19725                 }
19726
19727                 var entity = graph.hasEntity(id);
19728                 if (!entity || entity.type !== 'relation') { return; }
19729                 if (skipMultipolgonMembers && entity.isMultipolygon()) { return; }
19730                 entity.members
19731                     .map(function(member) { return member.id; })
19732                     .forEach(collectDeepDescendants);   // recurse
19733             }
19734         }
19735
19736
19737         // Adds or removes highlight styling for the specified entities
19738         function utilHighlightEntities(ids, highlighted, context) {
19739             context.surface()
19740                 .selectAll(utilEntityOrDeepMemberSelector(ids, context.graph()))
19741                 .classed('highlighted', highlighted);
19742         }
19743
19744
19745         // returns an Array that is the union of:
19746         //  - nodes for any nodeIDs passed in
19747         //  - child nodes of any wayIDs passed in
19748         //  - descendant member and child nodes of relationIDs passed in
19749         function utilGetAllNodes(ids, graph) {
19750             var seen = new Set();
19751             var nodes = new Set();
19752
19753             ids.forEach(collectNodes);
19754             return Array.from(nodes);
19755
19756             function collectNodes(id) {
19757                 if (seen.has(id)) { return; }
19758                 seen.add(id);
19759
19760                 var entity = graph.hasEntity(id);
19761                 if (!entity) { return; }
19762
19763                 if (entity.type === 'node') {
19764                     nodes.add(entity);
19765                 } else if (entity.type === 'way') {
19766                     entity.nodes.forEach(collectNodes);
19767                 } else {
19768                     entity.members
19769                         .map(function(member) { return member.id; })
19770                         .forEach(collectNodes);   // recurse
19771                 }
19772             }
19773         }
19774
19775
19776         function utilDisplayName(entity) {
19777             var localizedNameKey = 'name:' + _mainLocalizer.languageCode().toLowerCase();
19778             var name = entity.tags[localizedNameKey] || entity.tags.name || '';
19779             var network = entity.tags.cycle_network || entity.tags.network;
19780
19781             if (!name && entity.tags.ref) {
19782                 name = entity.tags.ref;
19783                 if (network) {
19784                     name = network + ' ' + name;
19785                 }
19786             }
19787
19788             return name;
19789         }
19790
19791
19792         function utilDisplayNameForPath(entity) {
19793             var name = utilDisplayName(entity);
19794             var isFirefox = utilDetect().browser.toLowerCase().indexOf('firefox') > -1;
19795
19796             if (!isFirefox && name && rtlRegex.test(name)) {
19797                 name = fixRTLTextForSvg(name);
19798             }
19799
19800             return name;
19801         }
19802
19803
19804         function utilDisplayType(id) {
19805             return {
19806                 n: _t('inspector.node'),
19807                 w: _t('inspector.way'),
19808                 r: _t('inspector.relation')
19809             }[id.charAt(0)];
19810         }
19811
19812
19813         function utilDisplayLabel(entity, graph) {
19814             var displayName = utilDisplayName(entity);
19815             if (displayName) {
19816                 // use the display name if there is one
19817                 return displayName;
19818             }
19819             var preset = _mainPresetIndex.match(entity, graph);
19820             if (preset && preset.name()) {
19821                 // use the preset name if there is a match
19822                 return preset.name();
19823             }
19824             // fallback to the display type (node/way/relation)
19825             return utilDisplayType(entity.id);
19826         }
19827
19828
19829         function utilEntityRoot(entityType) {
19830             return {
19831                 node: 'n',
19832                 way: 'w',
19833                 relation: 'r'
19834             }[entityType];
19835         }
19836
19837
19838         // Returns a single object containing the tags of all the given entities.
19839         // Example:
19840         // {
19841         //   highway: 'service',
19842         //   service: 'parking_aisle'
19843         // }
19844         //           +
19845         // {
19846         //   highway: 'service',
19847         //   service: 'driveway',
19848         //   width: '3'
19849         // }
19850         //           =
19851         // {
19852         //   highway: 'service',
19853         //   service: [ 'driveway', 'parking_aisle' ],
19854         //   width: [ '3', undefined ]
19855         // }
19856         function utilCombinedTags(entityIDs, graph) {
19857
19858             var tags = {};
19859             var tagCounts = {};
19860             var allKeys = new Set();
19861
19862             var entities = entityIDs.map(function(entityID) {
19863                 return graph.hasEntity(entityID);
19864             }).filter(Boolean);
19865
19866             // gather the aggregate keys
19867             entities.forEach(function(entity) {
19868                 var keys = Object.keys(entity.tags).filter(Boolean);
19869                 keys.forEach(function(key) {
19870                     allKeys.add(key);
19871                 });
19872             });
19873
19874             entities.forEach(function(entity) {
19875
19876                 allKeys.forEach(function(key) {
19877
19878                     var value = entity.tags[key]; // purposely allow `undefined`
19879
19880                     if (!tags.hasOwnProperty(key)) {
19881                         // first value, set as raw
19882                         tags[key] = value;
19883                     } else {
19884                         if (!Array.isArray(tags[key])) {
19885                             if (tags[key] !== value) {
19886                                 // first alternate value, replace single value with array
19887                                 tags[key] = [tags[key], value];
19888                             }
19889                         } else { // type is array
19890                             if (tags[key].indexOf(value) === -1) {
19891                                 // subsequent alternate value, add to array
19892                                 tags[key].push(value);
19893                             }
19894                         }
19895                     }
19896
19897                     var tagHash = key + '=' + value;
19898                     if (!tagCounts[tagHash]) { tagCounts[tagHash] = 0; }
19899                     tagCounts[tagHash] += 1;
19900                 });
19901             });
19902
19903             for (var key in tags) {
19904                 if (!Array.isArray(tags[key])) { continue; }
19905
19906                 // sort values by frequency then alphabetically
19907                 tags[key] = tags[key].sort(function(val1, val2) {
19908                     var key = key; // capture
19909                     var count2 = tagCounts[key + '=' + val2];
19910                     var count1 = tagCounts[key + '=' + val1];
19911                     if (count2 !== count1) {
19912                         return count2 - count1;
19913                     }
19914                     if (val2 && val1) {
19915                         return val1.localeCompare(val2);
19916                     }
19917                     return val1 ? 1 : -1;
19918                 });
19919             }
19920
19921             return tags;
19922         }
19923
19924
19925         function utilStringQs(str) {
19926             var i = 0;  // advance past any leading '?' or '#' characters
19927             while (i < str.length && (str[i] === '?' || str[i] === '#')) { i++; }
19928             str = str.slice(i);
19929
19930             return str.split('&').reduce(function(obj, pair){
19931                 var parts = pair.split('=');
19932                 if (parts.length === 2) {
19933                     obj[parts[0]] = (null === parts[1]) ? '' : decodeURIComponent(parts[1]);
19934                 }
19935                 return obj;
19936             }, {});
19937         }
19938
19939
19940         function utilQsString(obj, noencode) {
19941             // encode everything except special characters used in certain hash parameters:
19942             // "/" in map states, ":", ",", {" and "}" in background
19943             function softEncode(s) {
19944                 return encodeURIComponent(s).replace(/(%2F|%3A|%2C|%7B|%7D)/g, decodeURIComponent);
19945             }
19946
19947             return Object.keys(obj).sort().map(function(key) {
19948                 return encodeURIComponent(key) + '=' + (
19949                     noencode ? softEncode(obj[key]) : encodeURIComponent(obj[key]));
19950             }).join('&');
19951         }
19952
19953
19954         function utilPrefixDOMProperty(property) {
19955             var prefixes = ['webkit', 'ms', 'moz', 'o'];
19956             var i = -1;
19957             var n = prefixes.length;
19958             var s = document.body;
19959
19960             if (property in s)
19961                 { return property; }
19962
19963             property = property.substr(0, 1).toUpperCase() + property.substr(1);
19964
19965             while (++i < n) {
19966                 if (prefixes[i] + property in s) {
19967                     return prefixes[i] + property;
19968                 }
19969             }
19970
19971             return false;
19972         }
19973
19974
19975         function utilPrefixCSSProperty(property) {
19976             var prefixes = ['webkit', 'ms', 'Moz', 'O'];
19977             var i = -1;
19978             var n = prefixes.length;
19979             var s = document.body.style;
19980
19981             if (property.toLowerCase() in s) {
19982                 return property.toLowerCase();
19983             }
19984
19985             while (++i < n) {
19986                 if (prefixes[i] + property in s) {
19987                     return '-' + prefixes[i].toLowerCase() + property.replace(/([A-Z])/g, '-$1').toLowerCase();
19988                 }
19989             }
19990
19991             return false;
19992         }
19993
19994
19995         var transformProperty;
19996         function utilSetTransform(el, x, y, scale) {
19997             var prop = transformProperty = transformProperty || utilPrefixCSSProperty('Transform');
19998             var translate = utilDetect().opera ? 'translate('   + x + 'px,' + y + 'px)'
19999                 : 'translate3d(' + x + 'px,' + y + 'px,0)';
20000             return el.style(prop, translate + (scale ? ' scale(' + scale + ')' : ''));
20001         }
20002
20003
20004         // Calculates Levenshtein distance between two strings
20005         // see:  https://en.wikipedia.org/wiki/Levenshtein_distance
20006         // first converts the strings to lowercase and replaces diacritic marks with ascii equivalents.
20007         function utilEditDistance(a, b) {
20008             a = remove$1(a.toLowerCase());
20009             b = remove$1(b.toLowerCase());
20010             if (a.length === 0) { return b.length; }
20011             if (b.length === 0) { return a.length; }
20012             var matrix = [];
20013             for (var i = 0; i <= b.length; i++) { matrix[i] = [i]; }
20014             for (var j = 0; j <= a.length; j++) { matrix[0][j] = j; }
20015             for (i = 1; i <= b.length; i++) {
20016                 for (j = 1; j <= a.length; j++) {
20017                     if (b.charAt(i-1) === a.charAt(j-1)) {
20018                         matrix[i][j] = matrix[i-1][j-1];
20019                     } else {
20020                         matrix[i][j] = Math.min(matrix[i-1][j-1] + 1, // substitution
20021                             Math.min(matrix[i][j-1] + 1, // insertion
20022                             matrix[i-1][j] + 1)); // deletion
20023                     }
20024                 }
20025             }
20026             return matrix[b.length][a.length];
20027         }
20028
20029
20030         // a d3.mouse-alike which
20031         // 1. Only works on HTML elements, not SVG
20032         // 2. Does not cause style recalculation
20033         function utilFastMouse(container) {
20034             var rect = container.getBoundingClientRect();
20035             var rectLeft = rect.left;
20036             var rectTop = rect.top;
20037             var clientLeft = +container.clientLeft;
20038             var clientTop = +container.clientTop;
20039             return function(e) {
20040                 return [
20041                     e.clientX - rectLeft - clientLeft,
20042                     e.clientY - rectTop - clientTop];
20043             };
20044         }
20045
20046
20047         function utilAsyncMap(inputs, func, callback) {
20048             var remaining = inputs.length;
20049             var results = [];
20050             var errors = [];
20051
20052             inputs.forEach(function(d, i) {
20053                 func(d, function done(err, data) {
20054                     errors[i] = err;
20055                     results[i] = data;
20056                     remaining--;
20057                     if (!remaining) { callback(errors, results); }
20058                 });
20059             });
20060         }
20061
20062
20063         // wraps an index to an interval [0..length-1]
20064         function utilWrap(index, length) {
20065             if (index < 0) {
20066                 index += Math.ceil(-index/length)*length;
20067             }
20068             return index % length;
20069         }
20070
20071
20072         /**
20073          * a replacement for functor
20074          *
20075          * @param {*} value any value
20076          * @returns {Function} a function that returns that value or the value if it's a function
20077          */
20078         function utilFunctor(value) {
20079             if (typeof value === 'function') { return value; }
20080             return function() {
20081                 return value;
20082             };
20083         }
20084
20085
20086         function utilNoAuto(selection) {
20087             var isText = (selection.size() && selection.node().tagName.toLowerCase() === 'textarea');
20088
20089             return selection
20090                 // assign 'new-password' even for non-password fields to prevent browsers (Chrome) ignoring 'off'
20091                 .attr('autocomplete', 'new-password')
20092                 .attr('autocorrect', 'off')
20093                 .attr('autocapitalize', 'off')
20094                 .attr('spellcheck', isText ? 'true' : 'false');
20095         }
20096
20097
20098         // https://stackoverflow.com/questions/194846/is-there-any-kind-of-hash-code-function-in-javascript
20099         // https://werxltd.com/wp/2010/05/13/javascript-implementation-of-javas-string-hashcode-method/
20100         function utilHashcode(str) {
20101             var hash = 0;
20102             if (str.length === 0) {
20103                 return hash;
20104             }
20105             for (var i = 0; i < str.length; i++) {
20106                 var char = str.charCodeAt(i);
20107                 hash = ((hash << 5) - hash) + char;
20108                 hash = hash & hash; // Convert to 32bit integer
20109             }
20110             return hash;
20111         }
20112
20113         // Returns version of `str` with all runs of special characters replaced by `_`;
20114         // suitable for HTML ids, classes, selectors, etc.
20115         function utilSafeClassName(str) {
20116             return str.toLowerCase().replace(/[^a-z0-9]+/g, '_');
20117         }
20118
20119         // Returns string based on `val` that is highly unlikely to collide with an id
20120         // used previously or that's present elsewhere in the document. Useful for preventing
20121         // browser-provided autofills or when embedding iD on pages with unknown elements.
20122         function utilUniqueDomId(val) {
20123             return 'ideditor-' + utilSafeClassName(val.toString()) + '-' + new Date().getTime().toString();
20124         }
20125
20126         // Returns the length of `str` in unicode characters. This can be less than
20127         // `String.length()` since a single unicode character can be composed of multiple
20128         // JavaScript UTF-16 code units.
20129         function utilUnicodeCharsCount(str) {
20130             // Native ES2015 implementations of `Array.from` split strings into unicode characters
20131             return Array.from(str).length;
20132         }
20133
20134         // Returns a new string representing `str` cut from its start to `limit` length
20135         // in unicode characters. Note that this runs the risk of splitting graphemes.
20136         function utilUnicodeCharsTruncated(str, limit) {
20137             return Array.from(str).slice(0, limit).join('');
20138         }
20139
20140         function osmEntity(attrs) {
20141             // For prototypal inheritance.
20142             if (this instanceof osmEntity) { return; }
20143
20144             // Create the appropriate subtype.
20145             if (attrs && attrs.type) {
20146                 return osmEntity[attrs.type].apply(this, arguments);
20147             } else if (attrs && attrs.id) {
20148                 return osmEntity[osmEntity.id.type(attrs.id)].apply(this, arguments);
20149             }
20150
20151             // Initialize a generic Entity (used only in tests).
20152             return (new osmEntity()).initialize(arguments);
20153         }
20154
20155
20156         osmEntity.id = function(type) {
20157             return osmEntity.id.fromOSM(type, osmEntity.id.next[type]--);
20158         };
20159
20160
20161         osmEntity.id.next = {
20162             changeset: -1, node: -1, way: -1, relation: -1
20163         };
20164
20165
20166         osmEntity.id.fromOSM = function(type, id) {
20167             return type[0] + id;
20168         };
20169
20170
20171         osmEntity.id.toOSM = function(id) {
20172             return id.slice(1);
20173         };
20174
20175
20176         osmEntity.id.type = function(id) {
20177             return { 'c': 'changeset', 'n': 'node', 'w': 'way', 'r': 'relation' }[id[0]];
20178         };
20179
20180
20181         // A function suitable for use as the second argument to d3.selection#data().
20182         osmEntity.key = function(entity) {
20183             return entity.id + 'v' + (entity.v || 0);
20184         };
20185
20186         var _deprecatedTagValuesByKey;
20187
20188         osmEntity.deprecatedTagValuesByKey = function(dataDeprecated) {
20189             if (!_deprecatedTagValuesByKey) {
20190                 _deprecatedTagValuesByKey = {};
20191                 dataDeprecated.forEach(function(d) {
20192                     var oldKeys = Object.keys(d.old);
20193                     if (oldKeys.length === 1) {
20194                         var oldKey = oldKeys[0];
20195                         var oldValue = d.old[oldKey];
20196                         if (oldValue !== '*') {
20197                             if (!_deprecatedTagValuesByKey[oldKey]) {
20198                                 _deprecatedTagValuesByKey[oldKey] = [oldValue];
20199                             } else {
20200                                 _deprecatedTagValuesByKey[oldKey].push(oldValue);
20201                             }
20202                         }
20203                     }
20204                 });
20205             }
20206             return _deprecatedTagValuesByKey;
20207         };
20208
20209
20210         osmEntity.prototype = {
20211
20212             tags: {},
20213
20214
20215             initialize: function(sources) {
20216                 for (var i = 0; i < sources.length; ++i) {
20217                     var source = sources[i];
20218                     for (var prop in source) {
20219                         if (Object.prototype.hasOwnProperty.call(source, prop)) {
20220                             if (source[prop] === undefined) {
20221                                 delete this[prop];
20222                             } else {
20223                                 this[prop] = source[prop];
20224                             }
20225                         }
20226                     }
20227                 }
20228
20229                 if (!this.id && this.type) {
20230                     this.id = osmEntity.id(this.type);
20231                 }
20232                 if (!this.hasOwnProperty('visible')) {
20233                     this.visible = true;
20234                 }
20235
20236                 return this;
20237             },
20238
20239
20240             copy: function(resolver, copies) {
20241                 if (copies[this.id])
20242                     { return copies[this.id]; }
20243
20244                 var copy = osmEntity(this, { id: undefined, user: undefined, version: undefined });
20245                 copies[this.id] = copy;
20246
20247                 return copy;
20248             },
20249
20250
20251             osmId: function() {
20252                 return osmEntity.id.toOSM(this.id);
20253             },
20254
20255
20256             isNew: function() {
20257                 return this.osmId() < 0;
20258             },
20259
20260
20261             update: function(attrs) {
20262                 return osmEntity(this, attrs, { v: 1 + (this.v || 0) });
20263             },
20264
20265
20266             mergeTags: function(tags) {
20267                 var merged = Object.assign({}, this.tags);   // shallow copy
20268                 var changed = false;
20269                 for (var k in tags) {
20270                     var t1 = merged[k];
20271                     var t2 = tags[k];
20272                     if (!t1) {
20273                         changed = true;
20274                         merged[k] = t2;
20275                     } else if (t1 !== t2) {
20276                         changed = true;
20277                         merged[k] = utilUnicodeCharsTruncated(
20278                             utilArrayUnion(t1.split(/;\s*/), t2.split(/;\s*/)).join(';'),
20279                             255 // avoid exceeding character limit; see also services/osm.js -> maxCharsForTagValue()
20280                         );
20281                     }
20282                 }
20283                 return changed ? this.update({ tags: merged }) : this;
20284             },
20285
20286
20287             intersects: function(extent, resolver) {
20288                 return this.extent(resolver).intersects(extent);
20289             },
20290
20291
20292             hasNonGeometryTags: function() {
20293                 return Object.keys(this.tags).some(function(k) { return k !== 'area'; });
20294             },
20295
20296             hasParentRelations: function(resolver) {
20297                 return resolver.parentRelations(this).length > 0;
20298             },
20299
20300             hasInterestingTags: function() {
20301                 return Object.keys(this.tags).some(osmIsInterestingTag);
20302             },
20303
20304             hasWikidata: function() {
20305                 return !!this.tags.wikidata || !!this.tags['brand:wikidata'];
20306             },
20307
20308             isHighwayIntersection: function() {
20309                 return false;
20310             },
20311
20312             isDegenerate: function() {
20313                 return true;
20314             },
20315
20316             deprecatedTags: function(dataDeprecated) {
20317                 var tags = this.tags;
20318
20319                 // if there are no tags, none can be deprecated
20320                 if (Object.keys(tags).length === 0) { return []; }
20321
20322                 var deprecated = [];
20323                 dataDeprecated.forEach(function(d) {
20324                     var oldKeys = Object.keys(d.old);
20325                     var matchesDeprecatedTags = oldKeys.every(function(oldKey) {
20326                         if (!tags[oldKey]) { return false; }
20327                         if (d.old[oldKey] === '*') { return true; }
20328
20329                         var vals = tags[oldKey].split(';').filter(Boolean);
20330                         if (vals.length === 0) {
20331                             return false;
20332                         } else if (vals.length > 1) {
20333                             return vals.indexOf(d.old[oldKey]) !== -1;
20334                         } else {
20335                             if (tags[oldKey] === d.old[oldKey]) {
20336                                 if (d.replace && d.old[oldKey] === d.replace[oldKey]) {
20337                                     var replaceKeys = Object.keys(d.replace);
20338                                     return !replaceKeys.every(function(replaceKey) {
20339                                         return tags[replaceKey] === d.replace[replaceKey];
20340                                     });
20341                                 } else {
20342                                     return true;
20343                                 }
20344                             }
20345                         }
20346                         return false;
20347                     });
20348                     if (matchesDeprecatedTags) {
20349                         deprecated.push(d);
20350                     }
20351                 });
20352
20353                 return deprecated;
20354             }
20355         };
20356
20357         function osmLanes(entity) {
20358             if (entity.type !== 'way') { return null; }
20359             if (!entity.tags.highway) { return null; }
20360
20361             var tags = entity.tags;
20362             var isOneWay = entity.isOneWay();
20363             var laneCount = getLaneCount(tags, isOneWay);
20364             var maxspeed = parseMaxspeed(tags);
20365
20366             var laneDirections = parseLaneDirections(tags, isOneWay, laneCount);
20367             var forward = laneDirections.forward;
20368             var backward = laneDirections.backward;
20369             var bothways = laneDirections.bothways;
20370
20371             // parse the piped string 'x|y|z' format
20372             var turnLanes = {};
20373             turnLanes.unspecified = parseTurnLanes(tags['turn:lanes']);
20374             turnLanes.forward = parseTurnLanes(tags['turn:lanes:forward']);
20375             turnLanes.backward = parseTurnLanes(tags['turn:lanes:backward']);
20376
20377             var maxspeedLanes = {};
20378             maxspeedLanes.unspecified = parseMaxspeedLanes(tags['maxspeed:lanes'], maxspeed);
20379             maxspeedLanes.forward = parseMaxspeedLanes(tags['maxspeed:lanes:forward'], maxspeed);
20380             maxspeedLanes.backward = parseMaxspeedLanes(tags['maxspeed:lanes:backward'], maxspeed);
20381
20382             var psvLanes = {};
20383             psvLanes.unspecified = parseMiscLanes(tags['psv:lanes']);
20384             psvLanes.forward = parseMiscLanes(tags['psv:lanes:forward']);
20385             psvLanes.backward = parseMiscLanes(tags['psv:lanes:backward']);
20386
20387             var busLanes = {};
20388             busLanes.unspecified = parseMiscLanes(tags['bus:lanes']);
20389             busLanes.forward = parseMiscLanes(tags['bus:lanes:forward']);
20390             busLanes.backward = parseMiscLanes(tags['bus:lanes:backward']);
20391
20392             var taxiLanes = {};
20393             taxiLanes.unspecified = parseMiscLanes(tags['taxi:lanes']);
20394             taxiLanes.forward = parseMiscLanes(tags['taxi:lanes:forward']);
20395             taxiLanes.backward = parseMiscLanes(tags['taxi:lanes:backward']);
20396
20397             var hovLanes = {};
20398             hovLanes.unspecified = parseMiscLanes(tags['hov:lanes']);
20399             hovLanes.forward = parseMiscLanes(tags['hov:lanes:forward']);
20400             hovLanes.backward = parseMiscLanes(tags['hov:lanes:backward']);
20401
20402             var hgvLanes = {};
20403             hgvLanes.unspecified = parseMiscLanes(tags['hgv:lanes']);
20404             hgvLanes.forward = parseMiscLanes(tags['hgv:lanes:forward']);
20405             hgvLanes.backward = parseMiscLanes(tags['hgv:lanes:backward']);
20406
20407             var bicyclewayLanes = {};
20408             bicyclewayLanes.unspecified = parseBicycleWay(tags['bicycleway:lanes']);
20409             bicyclewayLanes.forward = parseBicycleWay(tags['bicycleway:lanes:forward']);
20410             bicyclewayLanes.backward = parseBicycleWay(tags['bicycleway:lanes:backward']);
20411
20412             var lanesObj = {
20413                 forward: [],
20414                 backward: [],
20415                 unspecified: []
20416             };
20417
20418             // map forward/backward/unspecified of each lane type to lanesObj
20419             mapToLanesObj(lanesObj, turnLanes, 'turnLane');
20420             mapToLanesObj(lanesObj, maxspeedLanes, 'maxspeed');
20421             mapToLanesObj(lanesObj, psvLanes, 'psv');
20422             mapToLanesObj(lanesObj, busLanes, 'bus');
20423             mapToLanesObj(lanesObj, taxiLanes, 'taxi');
20424             mapToLanesObj(lanesObj, hovLanes, 'hov');
20425             mapToLanesObj(lanesObj, hgvLanes, 'hgv');
20426             mapToLanesObj(lanesObj, bicyclewayLanes, 'bicycleway');
20427
20428             return {
20429                 metadata: {
20430                     count: laneCount,
20431                     oneway: isOneWay,
20432                     forward: forward,
20433                     backward: backward,
20434                     bothways: bothways,
20435                     turnLanes: turnLanes,
20436                     maxspeed: maxspeed,
20437                     maxspeedLanes: maxspeedLanes,
20438                     psvLanes: psvLanes,
20439                     busLanes: busLanes,
20440                     taxiLanes: taxiLanes,
20441                     hovLanes: hovLanes,
20442                     hgvLanes: hgvLanes,
20443                     bicyclewayLanes: bicyclewayLanes
20444                 },
20445                 lanes: lanesObj
20446             };
20447         }
20448
20449
20450         function getLaneCount(tags, isOneWay) {
20451             var count;
20452             if (tags.lanes) {
20453                 count = parseInt(tags.lanes, 10);
20454                 if (count > 0) {
20455                     return count;
20456                 }
20457             }
20458
20459
20460             switch (tags.highway) {
20461                 case 'trunk':
20462                 case 'motorway':
20463                     count = isOneWay ? 2 : 4;
20464                     break;
20465                 default:
20466                     count = isOneWay ? 1 : 2;
20467                     break;
20468             }
20469
20470             return count;
20471         }
20472
20473
20474         function parseMaxspeed(tags) {
20475             var maxspeed = tags.maxspeed;
20476             if (!maxspeed) { return; }
20477
20478             var maxspeedRegex = /^([0-9][\.0-9]+?)(?:[ ]?(?:km\/h|kmh|kph|mph|knots))?$/;
20479             if (!maxspeedRegex.test(maxspeed)) { return; }
20480
20481             return parseInt(maxspeed, 10);
20482         }
20483
20484
20485         function parseLaneDirections(tags, isOneWay, laneCount) {
20486             var forward = parseInt(tags['lanes:forward'], 10);
20487             var backward = parseInt(tags['lanes:backward'], 10);
20488             var bothways = parseInt(tags['lanes:both_ways'], 10) > 0 ? 1 : 0;
20489
20490             if (parseInt(tags.oneway, 10) === -1) {
20491                 forward = 0;
20492                 bothways = 0;
20493                 backward = laneCount;
20494             }
20495             else if (isOneWay) {
20496                 forward = laneCount;
20497                 bothways = 0;
20498                 backward = 0;
20499             }
20500             else if (isNaN(forward) && isNaN(backward)) {
20501                 backward = Math.floor((laneCount - bothways) / 2);
20502                 forward = laneCount - bothways - backward;
20503             }
20504             else if (isNaN(forward)) {
20505                 if (backward > laneCount - bothways) {
20506                     backward = laneCount - bothways;
20507                 }
20508                 forward = laneCount - bothways - backward;
20509             }
20510             else if (isNaN(backward)) {
20511                 if (forward > laneCount - bothways) {
20512                     forward = laneCount - bothways;
20513                 }
20514                 backward = laneCount - bothways - forward;
20515             }
20516             return {
20517                 forward: forward,
20518                 backward: backward,
20519                 bothways: bothways
20520             };
20521         }
20522
20523
20524         function parseTurnLanes(tag){
20525             if (!tag) { return; }
20526
20527             var validValues = [
20528                 'left', 'slight_left', 'sharp_left', 'through', 'right', 'slight_right',
20529                 'sharp_right', 'reverse', 'merge_to_left', 'merge_to_right', 'none'
20530             ];
20531
20532             return tag.split('|')
20533                 .map(function (s) {
20534                     if (s === '') { s = 'none'; }
20535                     return s.split(';')
20536                         .map(function (d) {
20537                             return validValues.indexOf(d) === -1 ? 'unknown': d;
20538                         });
20539                 });
20540         }
20541
20542
20543         function parseMaxspeedLanes(tag, maxspeed) {
20544             if (!tag) { return; }
20545
20546             return tag.split('|')
20547                 .map(function (s) {
20548                     if (s === 'none') { return s; }
20549                     var m = parseInt(s, 10);
20550                     if (s === '' || m === maxspeed) { return null; }
20551                     return isNaN(m) ? 'unknown': m;
20552                 });
20553         }
20554
20555
20556         function parseMiscLanes(tag) {
20557             if (!tag) { return; }
20558
20559             var validValues = [
20560                 'yes', 'no', 'designated'
20561             ];
20562
20563             return tag.split('|')
20564                 .map(function (s) {
20565                     if (s === '') { s = 'no'; }
20566                     return validValues.indexOf(s) === -1 ? 'unknown': s;
20567                 });
20568         }
20569
20570
20571         function parseBicycleWay(tag) {
20572             if (!tag) { return; }
20573
20574             var validValues = [
20575                 'yes', 'no', 'designated', 'lane'
20576             ];
20577
20578             return tag.split('|')
20579                 .map(function (s) {
20580                     if (s === '') { s = 'no'; }
20581                     return validValues.indexOf(s) === -1 ? 'unknown': s;
20582                 });
20583         }
20584
20585
20586         function mapToLanesObj(lanesObj, data, key) {
20587             if (data.forward) { data.forward.forEach(function(l, i) {
20588                 if (!lanesObj.forward[i]) { lanesObj.forward[i] = {}; }
20589                 lanesObj.forward[i][key] = l;
20590             }); }
20591             if (data.backward) { data.backward.forEach(function(l, i) {
20592                 if (!lanesObj.backward[i]) { lanesObj.backward[i] = {}; }
20593                 lanesObj.backward[i][key] = l;
20594             }); }
20595             if (data.unspecified) { data.unspecified.forEach(function(l, i) {
20596                 if (!lanesObj.unspecified[i]) { lanesObj.unspecified[i] = {}; }
20597                 lanesObj.unspecified[i][key] = l;
20598             }); }
20599         }
20600
20601         function osmWay() {
20602             if (!(this instanceof osmWay)) {
20603                 return (new osmWay()).initialize(arguments);
20604             } else if (arguments.length) {
20605                 this.initialize(arguments);
20606             }
20607         }
20608
20609
20610         osmEntity.way = osmWay;
20611
20612         osmWay.prototype = Object.create(osmEntity.prototype);
20613
20614
20615         Object.assign(osmWay.prototype, {
20616             type: 'way',
20617             nodes: [],
20618
20619
20620             copy: function(resolver, copies) {
20621                 if (copies[this.id]) { return copies[this.id]; }
20622
20623                 var copy = osmEntity.prototype.copy.call(this, resolver, copies);
20624
20625                 var nodes = this.nodes.map(function(id) {
20626                     return resolver.entity(id).copy(resolver, copies).id;
20627                 });
20628
20629                 copy = copy.update({ nodes: nodes });
20630                 copies[this.id] = copy;
20631
20632                 return copy;
20633             },
20634
20635
20636             extent: function(resolver) {
20637                 return resolver.transient(this, 'extent', function() {
20638                     var extent = geoExtent();
20639                     for (var i = 0; i < this.nodes.length; i++) {
20640                         var node = resolver.hasEntity(this.nodes[i]);
20641                         if (node) {
20642                             extent._extend(node.extent());
20643                         }
20644                     }
20645                     return extent;
20646                 });
20647             },
20648
20649
20650             first: function() {
20651                 return this.nodes[0];
20652             },
20653
20654
20655             last: function() {
20656                 return this.nodes[this.nodes.length - 1];
20657             },
20658
20659
20660             contains: function(node) {
20661                 return this.nodes.indexOf(node) >= 0;
20662             },
20663
20664
20665             affix: function(node) {
20666                 if (this.nodes[0] === node) { return 'prefix'; }
20667                 if (this.nodes[this.nodes.length - 1] === node) { return 'suffix'; }
20668             },
20669
20670
20671             layer: function() {
20672                 // explicit layer tag, clamp between -10, 10..
20673                 if (isFinite(this.tags.layer)) {
20674                     return Math.max(-10, Math.min(+(this.tags.layer), 10));
20675                 }
20676
20677                 // implied layer tag..
20678                 if (this.tags.covered === 'yes') { return -1; }
20679                 if (this.tags.location === 'overground') { return 1; }
20680                 if (this.tags.location === 'underground') { return -1; }
20681                 if (this.tags.location === 'underwater') { return -10; }
20682
20683                 if (this.tags.power === 'line') { return 10; }
20684                 if (this.tags.power === 'minor_line') { return 10; }
20685                 if (this.tags.aerialway) { return 10; }
20686                 if (this.tags.bridge) { return 1; }
20687                 if (this.tags.cutting) { return -1; }
20688                 if (this.tags.tunnel) { return -1; }
20689                 if (this.tags.waterway) { return -1; }
20690                 if (this.tags.man_made === 'pipeline') { return -10; }
20691                 if (this.tags.boundary) { return -10; }
20692                 return 0;
20693             },
20694
20695
20696             // the approximate width of the line based on its tags except its `width` tag
20697             impliedLineWidthMeters: function() {
20698                 var averageWidths = {
20699                     highway: { // width is for single lane
20700                         motorway: 5, motorway_link: 5, trunk: 4.5, trunk_link: 4.5,
20701                         primary: 4, secondary: 4, tertiary: 4,
20702                         primary_link: 4, secondary_link: 4, tertiary_link: 4,
20703                         unclassified: 4, road: 4, living_street: 4, bus_guideway: 4, pedestrian: 4,
20704                         residential: 3.5, service: 3.5, track: 3, cycleway: 2.5,
20705                         bridleway: 2, corridor: 2, steps: 2, path: 1.5, footway: 1.5
20706                     },
20707                     railway: { // width includes ties and rail bed, not just track gauge
20708                         rail: 2.5, light_rail: 2.5, tram: 2.5, subway: 2.5,
20709                         monorail: 2.5, funicular: 2.5, disused: 2.5, preserved: 2.5,
20710                         miniature: 1.5, narrow_gauge: 1.5
20711                     },
20712                     waterway: {
20713                         river: 50, canal: 25, stream: 5, tidal_channel: 5, fish_pass: 2.5, drain: 2.5, ditch: 1.5
20714                     }
20715                 };
20716                 for (var key in averageWidths) {
20717                     if (this.tags[key] && averageWidths[key][this.tags[key]]) {
20718                         var width = averageWidths[key][this.tags[key]];
20719                         if (key === 'highway') {
20720                             var laneCount = this.tags.lanes && parseInt(this.tags.lanes, 10);
20721                             if (!laneCount) { laneCount = this.isOneWay() ? 1 : 2; }
20722
20723                             return width * laneCount;
20724                         }
20725                         return width;
20726                     }
20727                 }
20728                 return null;
20729             },
20730
20731
20732             isOneWay: function() {
20733                 // explicit oneway tag..
20734                 var values = {
20735                     'yes': true,
20736                     '1': true,
20737                     '-1': true,
20738                     'reversible': true,
20739                     'alternating': true,
20740                     'no': false,
20741                     '0': false
20742                 };
20743                 if (values[this.tags.oneway] !== undefined) {
20744                     return values[this.tags.oneway];
20745                 }
20746
20747                 // implied oneway tag..
20748                 for (var key in this.tags) {
20749                     if (key in osmOneWayTags && (this.tags[key] in osmOneWayTags[key]))
20750                         { return true; }
20751                 }
20752                 return false;
20753             },
20754
20755             // Some identifier for tag that implies that this way is "sided",
20756             // i.e. the right side is the 'inside' (e.g. the right side of a
20757             // natural=cliff is lower).
20758             sidednessIdentifier: function() {
20759                 for (var key in this.tags) {
20760                     var value = this.tags[key];
20761                     if (key in osmRightSideIsInsideTags && (value in osmRightSideIsInsideTags[key])) {
20762                         if (osmRightSideIsInsideTags[key][value] === true) {
20763                             return key;
20764                         } else {
20765                             // if the map's value is something other than a
20766                             // literal true, we should use it so we can
20767                             // special case some keys (e.g. natural=coastline
20768                             // is handled differently to other naturals).
20769                             return osmRightSideIsInsideTags[key][value];
20770                         }
20771                     }
20772                 }
20773
20774                 return null;
20775             },
20776
20777             isSided: function() {
20778                 if (this.tags.two_sided === 'yes') {
20779                     return false;
20780                 }
20781
20782                 return this.sidednessIdentifier() !== null;
20783             },
20784
20785             lanes: function() {
20786                 return osmLanes(this);
20787             },
20788
20789
20790             isClosed: function() {
20791                 return this.nodes.length > 1 && this.first() === this.last();
20792             },
20793
20794
20795             isConvex: function(resolver) {
20796                 if (!this.isClosed() || this.isDegenerate()) { return null; }
20797
20798                 var nodes = utilArrayUniq(resolver.childNodes(this));
20799                 var coords = nodes.map(function(n) { return n.loc; });
20800                 var curr = 0;
20801                 var prev = 0;
20802
20803                 for (var i = 0; i < coords.length; i++) {
20804                     var o = coords[(i+1) % coords.length];
20805                     var a = coords[i];
20806                     var b = coords[(i+2) % coords.length];
20807                     var res = geoVecCross(a, b, o);
20808
20809                     curr = (res > 0) ? 1 : (res < 0) ? -1 : 0;
20810                     if (curr === 0) {
20811                         continue;
20812                     } else if (prev && curr !== prev) {
20813                         return false;
20814                     }
20815                     prev = curr;
20816                 }
20817                 return true;
20818             },
20819
20820             // returns an object with the tag that implies this is an area, if any
20821             tagSuggestingArea: function() {
20822                 return osmTagSuggestingArea(this.tags);
20823             },
20824
20825             isArea: function() {
20826                 if (this.tags.area === 'yes')
20827                     { return true; }
20828                 if (!this.isClosed() || this.tags.area === 'no')
20829                     { return false; }
20830                 return this.tagSuggestingArea() !== null;
20831             },
20832
20833
20834             isDegenerate: function() {
20835                 return (new Set(this.nodes).size < (this.isArea() ? 3 : 2));
20836             },
20837
20838
20839             areAdjacent: function(n1, n2) {
20840                 for (var i = 0; i < this.nodes.length; i++) {
20841                     if (this.nodes[i] === n1) {
20842                         if (this.nodes[i - 1] === n2) { return true; }
20843                         if (this.nodes[i + 1] === n2) { return true; }
20844                     }
20845                 }
20846                 return false;
20847             },
20848
20849
20850             geometry: function(graph) {
20851                 return graph.transient(this, 'geometry', function() {
20852                     return this.isArea() ? 'area' : 'line';
20853                 });
20854             },
20855
20856
20857             // returns an array of objects representing the segments between the nodes in this way
20858             segments: function(graph) {
20859
20860                 function segmentExtent(graph) {
20861                     var n1 = graph.hasEntity(this.nodes[0]);
20862                     var n2 = graph.hasEntity(this.nodes[1]);
20863                     return n1 && n2 && geoExtent([
20864                         [
20865                             Math.min(n1.loc[0], n2.loc[0]),
20866                             Math.min(n1.loc[1], n2.loc[1])
20867                         ],
20868                         [
20869                             Math.max(n1.loc[0], n2.loc[0]),
20870                             Math.max(n1.loc[1], n2.loc[1])
20871                         ]
20872                     ]);
20873                 }
20874
20875                 return graph.transient(this, 'segments', function() {
20876                     var segments = [];
20877                     for (var i = 0; i < this.nodes.length - 1; i++) {
20878                         segments.push({
20879                             id: this.id + '-' + i,
20880                             wayId: this.id,
20881                             index: i,
20882                             nodes: [this.nodes[i], this.nodes[i + 1]],
20883                             extent: segmentExtent
20884                         });
20885                     }
20886                     return segments;
20887                 });
20888             },
20889
20890
20891             // If this way is not closed, append the beginning node to the end of the nodelist to close it.
20892             close: function() {
20893                 if (this.isClosed() || !this.nodes.length) { return this; }
20894
20895                 var nodes = this.nodes.slice();
20896                 nodes = nodes.filter(noRepeatNodes);
20897                 nodes.push(nodes[0]);
20898                 return this.update({ nodes: nodes });
20899             },
20900
20901
20902             // If this way is closed, remove any connector nodes from the end of the nodelist to unclose it.
20903             unclose: function() {
20904                 if (!this.isClosed()) { return this; }
20905
20906                 var nodes = this.nodes.slice();
20907                 var connector = this.first();
20908                 var i = nodes.length - 1;
20909
20910                 // remove trailing connectors..
20911                 while (i > 0 && nodes.length > 1 && nodes[i] === connector) {
20912                     nodes.splice(i, 1);
20913                     i = nodes.length - 1;
20914                 }
20915
20916                 nodes = nodes.filter(noRepeatNodes);
20917                 return this.update({ nodes: nodes });
20918             },
20919
20920
20921             // Adds a node (id) in front of the node which is currently at position index.
20922             // If index is undefined, the node will be added to the end of the way for linear ways,
20923             //   or just before the final connecting node for circular ways.
20924             // Consecutive duplicates are eliminated including existing ones.
20925             // Circularity is always preserved when adding a node.
20926             addNode: function(id, index) {
20927                 var nodes = this.nodes.slice();
20928                 var isClosed = this.isClosed();
20929                 var max = isClosed ? nodes.length - 1 : nodes.length;
20930
20931                 if (index === undefined) {
20932                     index = max;
20933                 }
20934
20935                 if (index < 0 || index > max) {
20936                     throw new RangeError('index ' + index + ' out of range 0..' + max);
20937                 }
20938
20939                 // If this is a closed way, remove all connector nodes except the first one
20940                 // (there may be duplicates) and adjust index if necessary..
20941                 if (isClosed) {
20942                     var connector = this.first();
20943
20944                     // leading connectors..
20945                     var i = 1;
20946                     while (i < nodes.length && nodes.length > 2 && nodes[i] === connector) {
20947                         nodes.splice(i, 1);
20948                         if (index > i) { index--; }
20949                     }
20950
20951                     // trailing connectors..
20952                     i = nodes.length - 1;
20953                     while (i > 0 && nodes.length > 1 && nodes[i] === connector) {
20954                         nodes.splice(i, 1);
20955                         if (index > i) { index--; }
20956                         i = nodes.length - 1;
20957                     }
20958                 }
20959
20960                 nodes.splice(index, 0, id);
20961                 nodes = nodes.filter(noRepeatNodes);
20962
20963                 // If the way was closed before, append a connector node to keep it closed..
20964                 if (isClosed && (nodes.length === 1 || nodes[0] !== nodes[nodes.length - 1])) {
20965                     nodes.push(nodes[0]);
20966                 }
20967
20968                 return this.update({ nodes: nodes });
20969             },
20970
20971
20972             // Replaces the node which is currently at position index with the given node (id).
20973             // Consecutive duplicates are eliminated including existing ones.
20974             // Circularity is preserved when updating a node.
20975             updateNode: function(id, index) {
20976                 var nodes = this.nodes.slice();
20977                 var isClosed = this.isClosed();
20978                 var max = nodes.length - 1;
20979
20980                 if (index === undefined || index < 0 || index > max) {
20981                     throw new RangeError('index ' + index + ' out of range 0..' + max);
20982                 }
20983
20984                 // If this is a closed way, remove all connector nodes except the first one
20985                 // (there may be duplicates) and adjust index if necessary..
20986                 if (isClosed) {
20987                     var connector = this.first();
20988
20989                     // leading connectors..
20990                     var i = 1;
20991                     while (i < nodes.length && nodes.length > 2 && nodes[i] === connector) {
20992                         nodes.splice(i, 1);
20993                         if (index > i) { index--; }
20994                     }
20995
20996                     // trailing connectors..
20997                     i = nodes.length - 1;
20998                     while (i > 0 && nodes.length > 1 && nodes[i] === connector) {
20999                         nodes.splice(i, 1);
21000                         if (index === i) { index = 0; }  // update leading connector instead
21001                         i = nodes.length - 1;
21002                     }
21003                 }
21004
21005                 nodes.splice(index, 1, id);
21006                 nodes = nodes.filter(noRepeatNodes);
21007
21008                 // If the way was closed before, append a connector node to keep it closed..
21009                 if (isClosed && (nodes.length === 1 || nodes[0] !== nodes[nodes.length - 1])) {
21010                     nodes.push(nodes[0]);
21011                 }
21012
21013                 return this.update({nodes: nodes});
21014             },
21015
21016
21017             // Replaces each occurrence of node id needle with replacement.
21018             // Consecutive duplicates are eliminated including existing ones.
21019             // Circularity is preserved.
21020             replaceNode: function(needleID, replacementID) {
21021                 var nodes = this.nodes.slice();
21022                 var isClosed = this.isClosed();
21023
21024                 for (var i = 0; i < nodes.length; i++) {
21025                     if (nodes[i] === needleID) {
21026                         nodes[i] = replacementID;
21027                     }
21028                 }
21029
21030                 nodes = nodes.filter(noRepeatNodes);
21031
21032                 // If the way was closed before, append a connector node to keep it closed..
21033                 if (isClosed && (nodes.length === 1 || nodes[0] !== nodes[nodes.length - 1])) {
21034                     nodes.push(nodes[0]);
21035                 }
21036
21037                 return this.update({nodes: nodes});
21038             },
21039
21040
21041             // Removes each occurrence of node id.
21042             // Consecutive duplicates are eliminated including existing ones.
21043             // Circularity is preserved.
21044             removeNode: function(id) {
21045                 var nodes = this.nodes.slice();
21046                 var isClosed = this.isClosed();
21047
21048                 nodes = nodes
21049                     .filter(function(node) { return node !== id; })
21050                     .filter(noRepeatNodes);
21051
21052                 // If the way was closed before, append a connector node to keep it closed..
21053                 if (isClosed && (nodes.length === 1 || nodes[0] !== nodes[nodes.length - 1])) {
21054                     nodes.push(nodes[0]);
21055                 }
21056
21057                 return this.update({nodes: nodes});
21058             },
21059
21060
21061             asJXON: function(changeset_id) {
21062                 var r = {
21063                     way: {
21064                         '@id': this.osmId(),
21065                         '@version': this.version || 0,
21066                         nd: this.nodes.map(function(id) {
21067                             return { keyAttributes: { ref: osmEntity.id.toOSM(id) } };
21068                         }, this),
21069                         tag: Object.keys(this.tags).map(function(k) {
21070                             return { keyAttributes: { k: k, v: this.tags[k] } };
21071                         }, this)
21072                     }
21073                 };
21074                 if (changeset_id) {
21075                     r.way['@changeset'] = changeset_id;
21076                 }
21077                 return r;
21078             },
21079
21080
21081             asGeoJSON: function(resolver) {
21082                 return resolver.transient(this, 'GeoJSON', function() {
21083                     var coordinates = resolver.childNodes(this)
21084                         .map(function(n) { return n.loc; });
21085
21086                     if (this.isArea() && this.isClosed()) {
21087                         return {
21088                             type: 'Polygon',
21089                             coordinates: [coordinates]
21090                         };
21091                     } else {
21092                         return {
21093                             type: 'LineString',
21094                             coordinates: coordinates
21095                         };
21096                     }
21097                 });
21098             },
21099
21100
21101             area: function(resolver) {
21102                 return resolver.transient(this, 'area', function() {
21103                     var nodes = resolver.childNodes(this);
21104
21105                     var json = {
21106                         type: 'Polygon',
21107                         coordinates: [ nodes.map(function(n) { return n.loc; }) ]
21108                     };
21109
21110                     if (!this.isClosed() && nodes.length) {
21111                         json.coordinates[0].push(nodes[0].loc);
21112                     }
21113
21114                     var area = d3_geoArea(json);
21115
21116                     // Heuristic for detecting counterclockwise winding order. Assumes
21117                     // that OpenStreetMap polygons are not hemisphere-spanning.
21118                     if (area > 2 * Math.PI) {
21119                         json.coordinates[0] = json.coordinates[0].reverse();
21120                         area = d3_geoArea(json);
21121                     }
21122
21123                     return isNaN(area) ? 0 : area;
21124                 });
21125             }
21126         });
21127
21128
21129         // Filter function to eliminate consecutive duplicates.
21130         function noRepeatNodes(node, i, arr) {
21131             return i === 0 || node !== arr[i - 1];
21132         }
21133
21134         // "Old" multipolyons, previously known as "simple" multipolygons, are as follows:
21135         //
21136         // 1. Relation tagged with `type=multipolygon` and no interesting tags.
21137         // 2. One and only one member with the `outer` role. Must be a way with interesting tags.
21138         // 3. No members without a role.
21139         //
21140         // Old multipolygons are no longer recommended but are still rendered as areas by iD.
21141
21142         function osmOldMultipolygonOuterMemberOfRelation(entity, graph) {
21143             if (entity.type !== 'relation' ||
21144                 !entity.isMultipolygon()
21145                 || Object.keys(entity.tags).filter(osmIsInterestingTag).length > 1) {
21146                 return false;
21147             }
21148
21149             var outerMember;
21150             for (var memberIndex in entity.members) {
21151                 var member = entity.members[memberIndex];
21152                 if (!member.role || member.role === 'outer') {
21153                     if (outerMember) { return false; }
21154                     if (member.type !== 'way') { return false; }
21155                     if (!graph.hasEntity(member.id)) { return false; }
21156
21157                     outerMember = graph.entity(member.id);
21158
21159                     if (Object.keys(outerMember.tags).filter(osmIsInterestingTag).length === 0) {
21160                         return false;
21161                     }
21162                 }
21163             }
21164
21165             return outerMember;
21166         }
21167
21168         // For fixing up rendering of multipolygons with tags on the outer member.
21169         // https://github.com/openstreetmap/iD/issues/613
21170         function osmIsOldMultipolygonOuterMember(entity, graph) {
21171             if (entity.type !== 'way' || Object.keys(entity.tags).filter(osmIsInterestingTag).length === 0)
21172                 { return false; }
21173
21174             var parents = graph.parentRelations(entity);
21175             if (parents.length !== 1)
21176                 { return false; }
21177
21178             var parent = parents[0];
21179             if (!parent.isMultipolygon() || Object.keys(parent.tags).filter(osmIsInterestingTag).length > 1)
21180                 { return false; }
21181
21182             var members = parent.members, member;
21183             for (var i = 0; i < members.length; i++) {
21184                 member = members[i];
21185                 if (member.id === entity.id && member.role && member.role !== 'outer')
21186                     { return false; } // Not outer member
21187                 if (member.id !== entity.id && (!member.role || member.role === 'outer'))
21188                     { return false; } // Not a simple multipolygon
21189             }
21190
21191             return parent;
21192         }
21193
21194
21195         function osmOldMultipolygonOuterMember(entity, graph) {
21196             if (entity.type !== 'way')
21197                 { return false; }
21198
21199             var parents = graph.parentRelations(entity);
21200             if (parents.length !== 1)
21201                 { return false; }
21202
21203             var parent = parents[0];
21204             if (!parent.isMultipolygon() || Object.keys(parent.tags).filter(osmIsInterestingTag).length > 1)
21205                 { return false; }
21206
21207             var members = parent.members, member, outerMember;
21208             for (var i = 0; i < members.length; i++) {
21209                 member = members[i];
21210                 if (!member.role || member.role === 'outer') {
21211                     if (outerMember)
21212                         { return false; } // Not a simple multipolygon
21213                     outerMember = member;
21214                 }
21215             }
21216
21217             if (!outerMember)
21218                 { return false; }
21219
21220             var outerEntity = graph.hasEntity(outerMember.id);
21221             if (!outerEntity || !Object.keys(outerEntity.tags).filter(osmIsInterestingTag).length)
21222                 { return false; }
21223
21224             return outerEntity;
21225         }
21226
21227
21228         // Join `toJoin` array into sequences of connecting ways.
21229
21230         // Segments which share identical start/end nodes will, as much as possible,
21231         // be connected with each other.
21232         //
21233         // The return value is a nested array. Each constituent array contains elements
21234         // of `toJoin` which have been determined to connect.
21235         //
21236         // Each consitituent array also has a `nodes` property whose value is an
21237         // ordered array of member nodes, with appropriate order reversal and
21238         // start/end coordinate de-duplication.
21239         //
21240         // Members of `toJoin` must have, at minimum, `type` and `id` properties.
21241         // Thus either an array of `osmWay`s or a relation member array may be used.
21242         //
21243         // If an member is an `osmWay`, its tags and childnodes may be reversed via
21244         // `actionReverse` in the output.
21245         //
21246         // The returned sequences array also has an `actions` array property, containing
21247         // any reversal actions that should be applied to the graph, should the calling
21248         // code attempt to actually join the given ways.
21249         //
21250         // Incomplete members (those for which `graph.hasEntity(element.id)` returns
21251         // false) and non-way members are ignored.
21252         //
21253         function osmJoinWays(toJoin, graph) {
21254             function resolve(member) {
21255                 return graph.childNodes(graph.entity(member.id));
21256             }
21257
21258             function reverse(item) {
21259                 var action = actionReverse(item.id, { reverseOneway: true });
21260                 sequences.actions.push(action);
21261                 return (item instanceof osmWay) ? action(graph).entity(item.id) : item;
21262             }
21263
21264             // make a copy containing only the items to join
21265             toJoin = toJoin.filter(function(member) {
21266                 return member.type === 'way' && graph.hasEntity(member.id);
21267             });
21268
21269             // Are the things we are joining relation members or `osmWays`?
21270             // If `osmWays`, skip the "prefer a forward path" code below (see #4872)
21271             var i;
21272             var joinAsMembers = true;
21273             for (i = 0; i < toJoin.length; i++) {
21274                 if (toJoin[i] instanceof osmWay) {
21275                     joinAsMembers = false;
21276                     break;
21277                 }
21278             }
21279
21280             var sequences = [];
21281             sequences.actions = [];
21282
21283             while (toJoin.length) {
21284                 // start a new sequence
21285                 var item = toJoin.shift();
21286                 var currWays = [item];
21287                 var currNodes = resolve(item).slice();
21288                 var doneSequence = false;
21289
21290                 // add to it
21291                 while (toJoin.length && !doneSequence) {
21292                     var start = currNodes[0];
21293                     var end = currNodes[currNodes.length - 1];
21294                     var fn = null;
21295                     var nodes = null;
21296
21297                     // Find the next way/member to join.
21298                     for (i = 0; i < toJoin.length; i++) {
21299                         item = toJoin[i];
21300                         nodes = resolve(item);
21301
21302                         // (for member ordering only, not way ordering - see #4872)
21303                         // Strongly prefer to generate a forward path that preserves the order
21304                         // of the members array. For multipolygons and most relations, member
21305                         // order does not matter - but for routes, it does. (see #4589)
21306                         // If we started this sequence backwards (i.e. next member way attaches to
21307                         // the start node and not the end node), reverse the initial way before continuing.
21308                         if (joinAsMembers && currWays.length === 1 && nodes[0] !== end && nodes[nodes.length - 1] !== end &&
21309                             (nodes[nodes.length - 1] === start || nodes[0] === start)
21310                         ) {
21311                             currWays[0] = reverse(currWays[0]);
21312                             currNodes.reverse();
21313                             start = currNodes[0];
21314                             end = currNodes[currNodes.length - 1];
21315                         }
21316
21317                         if (nodes[0] === end) {
21318                             fn = currNodes.push;               // join to end
21319                             nodes = nodes.slice(1);
21320                             break;
21321                         } else if (nodes[nodes.length - 1] === end) {
21322                             fn = currNodes.push;               // join to end
21323                             nodes = nodes.slice(0, -1).reverse();
21324                             item = reverse(item);
21325                             break;
21326                         } else if (nodes[nodes.length - 1] === start) {
21327                             fn = currNodes.unshift;            // join to beginning
21328                             nodes = nodes.slice(0, -1);
21329                             break;
21330                         } else if (nodes[0] === start) {
21331                             fn = currNodes.unshift;            // join to beginning
21332                             nodes = nodes.slice(1).reverse();
21333                             item = reverse(item);
21334                             break;
21335                         } else {
21336                             fn = nodes = null;
21337                         }
21338                     }
21339
21340                     if (!nodes) {     // couldn't find a joinable way/member
21341                         doneSequence = true;
21342                         break;
21343                     }
21344
21345                     fn.apply(currWays, [item]);
21346                     fn.apply(currNodes, nodes);
21347
21348                     toJoin.splice(i, 1);
21349                 }
21350
21351                 currWays.nodes = currNodes;
21352                 sequences.push(currWays);
21353             }
21354
21355             return sequences;
21356         }
21357
21358         function actionAddMember(relationId, member, memberIndex, insertPair) {
21359
21360             return function action(graph) {
21361                 var relation = graph.entity(relationId);
21362
21363                 // There are some special rules for Public Transport v2 routes.
21364                 var isPTv2 = /stop|platform/.test(member.role);
21365
21366                 if ((isNaN(memberIndex) || insertPair) && member.type === 'way' && !isPTv2) {
21367                     // Try to perform sensible inserts based on how the ways join together
21368                     graph = addWayMember(relation, graph);
21369                 } else {
21370                     // see https://wiki.openstreetmap.org/wiki/Public_transport#Service_routes
21371                     // Stops and Platforms for PTv2 should be ordered first.
21372                     // hack: We do not currently have the ability to place them in the exactly correct order.
21373                     if (isPTv2 && isNaN(memberIndex)) {
21374                         memberIndex = 0;
21375                     }
21376
21377                     graph = graph.replace(relation.addMember(member, memberIndex));
21378                 }
21379
21380                 return graph;
21381             };
21382
21383
21384             // Add a way member into the relation "wherever it makes sense".
21385             // In this situation we were not supplied a memberIndex.
21386             function addWayMember(relation, graph) {
21387                 var groups, tempWay, item, i, j, k;
21388
21389                 // remove PTv2 stops and platforms before doing anything.
21390                 var PTv2members = [];
21391                 var members = [];
21392                 for (i = 0; i < relation.members.length; i++) {
21393                     var m = relation.members[i];
21394                     if (/stop|platform/.test(m.role)) {
21395                         PTv2members.push(m);
21396                     } else {
21397                         members.push(m);
21398                     }
21399                 }
21400                 relation = relation.update({ members: members });
21401
21402
21403                 if (insertPair) {
21404                     // We're adding a member that must stay paired with an existing member.
21405                     // (This feature is used by `actionSplit`)
21406                     //
21407                     // This is tricky because the members may exist multiple times in the
21408                     // member list, and with different A-B/B-A ordering and different roles.
21409                     // (e.g. a bus route that loops out and back - #4589).
21410                     //
21411                     // Replace the existing member with a temporary way,
21412                     // so that `osmJoinWays` can treat the pair like a single way.
21413                     tempWay = osmWay({ id: 'wTemp', nodes: insertPair.nodes });
21414                     graph = graph.replace(tempWay);
21415                     var tempMember = { id: tempWay.id, type: 'way', role: member.role };
21416                     var tempRelation = relation.replaceMember({id: insertPair.originalID}, tempMember, true);
21417                     groups = utilArrayGroupBy(tempRelation.members, 'type');
21418                     groups.way = groups.way || [];
21419
21420                 } else {
21421                     // Add the member anywhere, one time. Just push and let `osmJoinWays` decide where to put it.
21422                     groups = utilArrayGroupBy(relation.members, 'type');
21423                     groups.way = groups.way || [];
21424                     groups.way.push(member);
21425                 }
21426
21427                 members = withIndex(groups.way);
21428                 var joined = osmJoinWays(members, graph);
21429
21430                 // `joined` might not contain all of the way members,
21431                 // But will contain only the completed (downloaded) members
21432                 for (i = 0; i < joined.length; i++) {
21433                     var segment = joined[i];
21434                     var nodes = segment.nodes.slice();
21435                     var startIndex = segment[0].index;
21436
21437                     // j = array index in `members` where this segment starts
21438                     for (j = 0; j < members.length; j++) {
21439                         if (members[j].index === startIndex) {
21440                             break;
21441                         }
21442                     }
21443
21444                     // k = each member in segment
21445                     for (k = 0; k < segment.length; k++) {
21446                         item = segment[k];
21447                         var way = graph.entity(item.id);
21448
21449                         // If this is a paired item, generate members in correct order and role
21450                         if (tempWay && item.id === tempWay.id) {
21451                             if (nodes[0].id === insertPair.nodes[0]) {
21452                                 item.pair = [
21453                                     { id: insertPair.originalID, type: 'way', role: item.role },
21454                                     { id: insertPair.insertedID, type: 'way', role: item.role }
21455                                 ];
21456                             } else {
21457                                 item.pair = [
21458                                     { id: insertPair.insertedID, type: 'way', role: item.role },
21459                                     { id: insertPair.originalID, type: 'way', role: item.role }
21460                                 ];
21461                             }
21462                         }
21463
21464                         // reorder `members` if necessary
21465                         if (k > 0) {
21466                             if (j+k >= members.length || item.index !== members[j+k].index) {
21467                                 moveMember(members, item.index, j+k);
21468                             }
21469                         }
21470
21471                         nodes.splice(0, way.nodes.length - 1);
21472                     }
21473                 }
21474
21475                 if (tempWay) {
21476                     graph = graph.remove(tempWay);
21477                 }
21478
21479                 // Final pass: skip dead items, split pairs, remove index properties
21480                 var wayMembers = [];
21481                 for (i = 0; i < members.length; i++) {
21482                     item = members[i];
21483                     if (item.index === -1) { continue; }
21484
21485                     if (item.pair) {
21486                         wayMembers.push(item.pair[0]);
21487                         wayMembers.push(item.pair[1]);
21488                     } else {
21489                         wayMembers.push(utilObjectOmit(item, ['index']));
21490                     }
21491                 }
21492
21493                 // Put stops and platforms first, then nodes, ways, relations
21494                 // This is recommended for Public Transport v2 routes:
21495                 // see https://wiki.openstreetmap.org/wiki/Public_transport#Service_routes
21496                 var newMembers = PTv2members.concat( (groups.node || []), wayMembers, (groups.relation || []) );
21497
21498                 return graph.replace(relation.update({ members: newMembers }));
21499
21500
21501                 // `moveMember()` changes the `members` array in place by splicing
21502                 // the item with `.index = findIndex` to where it belongs,
21503                 // and marking the old position as "dead" with `.index = -1`
21504                 //
21505                 // j=5, k=0                jk
21506                 // segment                 5 4 7 6
21507                 // members       0 1 2 3 4 5 6 7 8 9        keep 5 in j+k
21508                 //
21509                 // j=5, k=1                j k
21510                 // segment                 5 4 7 6
21511                 // members       0 1 2 3 4 5 6 7 8 9        move 4 to j+k
21512                 // members       0 1 2 3 x 5 4 6 7 8 9      moved
21513                 //
21514                 // j=5, k=2                j   k
21515                 // segment                 5 4 7 6
21516                 // members       0 1 2 3 x 5 4 6 7 8 9      move 7 to j+k
21517                 // members       0 1 2 3 x 5 4 7 6 x 8 9    moved
21518                 //
21519                 // j=5, k=3                j     k
21520                 // segment                 5 4 7 6
21521                 // members       0 1 2 3 x 5 4 7 6 x 8 9    keep 6 in j+k
21522                 //
21523                 function moveMember(arr, findIndex, toIndex) {
21524                     for (var i = 0; i < arr.length; i++) {
21525                         if (arr[i].index === findIndex) {
21526                             break;
21527                         }
21528                     }
21529
21530                     var item = Object.assign({}, arr[i]);   // shallow copy
21531                     arr[i].index = -1;   // mark as dead
21532                     item.index = toIndex;
21533                     arr.splice(toIndex, 0, item);
21534                 }
21535
21536
21537                 // This is the same as `Relation.indexedMembers`,
21538                 // Except we don't want to index all the members, only the ways
21539                 function withIndex(arr) {
21540                     var result = new Array(arr.length);
21541                     for (var i = 0; i < arr.length; i++) {
21542                         result[i] = Object.assign({}, arr[i]);   // shallow copy
21543                         result[i].index = i;
21544                     }
21545                     return result;
21546                 }
21547             }
21548
21549         }
21550
21551         function actionAddMidpoint(midpoint, node) {
21552             return function(graph) {
21553                 graph = graph.replace(node.move(midpoint.loc));
21554
21555                 var parents = utilArrayIntersection(
21556                     graph.parentWays(graph.entity(midpoint.edge[0])),
21557                     graph.parentWays(graph.entity(midpoint.edge[1]))
21558                 );
21559
21560                 parents.forEach(function(way) {
21561                     for (var i = 0; i < way.nodes.length - 1; i++) {
21562                         if (geoEdgeEqual([way.nodes[i], way.nodes[i + 1]], midpoint.edge)) {
21563                             graph = graph.replace(graph.entity(way.id).addNode(node.id, i + 1));
21564
21565                             // Add only one midpoint on doubled-back segments,
21566                             // turning them into self-intersections.
21567                             return;
21568                         }
21569                     }
21570                 });
21571
21572                 return graph;
21573             };
21574         }
21575
21576         // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/AddNodeToWayAction.as
21577         function actionAddVertex(wayId, nodeId, index) {
21578             return function(graph) {
21579                 return graph.replace(graph.entity(wayId).addNode(nodeId, index));
21580             };
21581         }
21582
21583         function actionChangeMember(relationId, member, memberIndex) {
21584             return function(graph) {
21585                 return graph.replace(graph.entity(relationId).updateMember(member, memberIndex));
21586             };
21587         }
21588
21589         function actionChangePreset(entityID, oldPreset, newPreset, skipFieldDefaults) {
21590             return function action(graph) {
21591                 var entity = graph.entity(entityID);
21592                 var geometry = entity.geometry(graph);
21593                 var tags = entity.tags;
21594
21595                 if (oldPreset) { tags = oldPreset.unsetTags(tags, geometry); }
21596                 if (newPreset) { tags = newPreset.setTags(tags, geometry, skipFieldDefaults); }
21597
21598                 return graph.replace(entity.update({tags: tags}));
21599             };
21600         }
21601
21602         function actionChangeTags(entityId, tags) {
21603             return function(graph) {
21604                 var entity = graph.entity(entityId);
21605                 return graph.replace(entity.update({tags: tags}));
21606             };
21607         }
21608
21609         function osmNode() {
21610             if (!(this instanceof osmNode)) {
21611                 return (new osmNode()).initialize(arguments);
21612             } else if (arguments.length) {
21613                 this.initialize(arguments);
21614             }
21615         }
21616
21617         osmEntity.node = osmNode;
21618
21619         osmNode.prototype = Object.create(osmEntity.prototype);
21620
21621         Object.assign(osmNode.prototype, {
21622             type: 'node',
21623             loc: [9999, 9999],
21624
21625             extent: function() {
21626                 return new geoExtent(this.loc);
21627             },
21628
21629
21630             geometry: function(graph) {
21631                 return graph.transient(this, 'geometry', function() {
21632                     return graph.isPoi(this) ? 'point' : 'vertex';
21633                 });
21634             },
21635
21636
21637             move: function(loc) {
21638                 return this.update({loc: loc});
21639             },
21640
21641
21642             isDegenerate: function() {
21643                 return !(
21644                     Array.isArray(this.loc) && this.loc.length === 2 &&
21645                     this.loc[0] >= -180 && this.loc[0] <= 180 &&
21646                     this.loc[1] >= -90 && this.loc[1] <= 90
21647                 );
21648             },
21649
21650
21651             // Inspect tags and geometry to determine which direction(s) this node/vertex points
21652             directions: function(resolver, projection) {
21653                 var val;
21654                 var i;
21655
21656                 // which tag to use?
21657                 if (this.isHighwayIntersection(resolver) && (this.tags.stop || '').toLowerCase() === 'all') {
21658                     // all-way stop tag on a highway intersection
21659                     val = 'all';
21660                 } else {
21661                     // generic direction tag
21662                     val = (this.tags.direction || '').toLowerCase();
21663
21664                     // better suffix-style direction tag
21665                     var re = /:direction$/i;
21666                     var keys = Object.keys(this.tags);
21667                     for (i = 0; i < keys.length; i++) {
21668                         if (re.test(keys[i])) {
21669                             val = this.tags[keys[i]].toLowerCase();
21670                             break;
21671                         }
21672                     }
21673                 }
21674
21675                 if (val === '') { return []; }
21676
21677                 var cardinal = {
21678                     north: 0,               n: 0,
21679                     northnortheast: 22,     nne: 22,
21680                     northeast: 45,          ne: 45,
21681                     eastnortheast: 67,      ene: 67,
21682                     east: 90,               e: 90,
21683                     eastsoutheast: 112,     ese: 112,
21684                     southeast: 135,         se: 135,
21685                     southsoutheast: 157,    sse: 157,
21686                     south: 180,             s: 180,
21687                     southsouthwest: 202,    ssw: 202,
21688                     southwest: 225,         sw: 225,
21689                     westsouthwest: 247,     wsw: 247,
21690                     west: 270,              w: 270,
21691                     westnorthwest: 292,     wnw: 292,
21692                     northwest: 315,         nw: 315,
21693                     northnorthwest: 337,    nnw: 337
21694                 };
21695
21696
21697                 var values = val.split(';');
21698                 var results = [];
21699
21700                 values.forEach(function(v) {
21701                     // swap cardinal for numeric directions
21702                     if (cardinal[v] !== undefined) {
21703                         v = cardinal[v];
21704                     }
21705
21706                     // numeric direction - just add to results
21707                     if (v !== '' && !isNaN(+v)) {
21708                         results.push(+v);
21709                         return;
21710                     }
21711
21712                     // string direction - inspect parent ways
21713                     var lookBackward =
21714                         (this.tags['traffic_sign:backward'] || v === 'backward' || v === 'both' || v === 'all');
21715                     var lookForward =
21716                         (this.tags['traffic_sign:forward'] || v === 'forward' || v === 'both' || v === 'all');
21717
21718                     if (!lookForward && !lookBackward) { return; }
21719
21720                     var nodeIds = {};
21721                     resolver.parentWays(this).forEach(function(parent) {
21722                         var nodes = parent.nodes;
21723                         for (i = 0; i < nodes.length; i++) {
21724                             if (nodes[i] === this.id) {  // match current entity
21725                                 if (lookForward && i > 0) {
21726                                     nodeIds[nodes[i - 1]] = true;  // look back to prev node
21727                                 }
21728                                 if (lookBackward && i < nodes.length - 1) {
21729                                     nodeIds[nodes[i + 1]] = true;  // look ahead to next node
21730                                 }
21731                             }
21732                         }
21733                     }, this);
21734
21735                     Object.keys(nodeIds).forEach(function(nodeId) {
21736                         // +90 because geoAngle returns angle from X axis, not Y (north)
21737                         results.push(
21738                             (geoAngle(this, resolver.entity(nodeId), projection) * (180 / Math.PI)) + 90
21739                         );
21740                     }, this);
21741
21742                 }, this);
21743
21744                 return utilArrayUniq(results);
21745             },
21746
21747
21748             isEndpoint: function(resolver) {
21749                 return resolver.transient(this, 'isEndpoint', function() {
21750                     var id = this.id;
21751                     return resolver.parentWays(this).filter(function(parent) {
21752                         return !parent.isClosed() && !!parent.affix(id);
21753                     }).length > 0;
21754                 });
21755             },
21756
21757
21758             isConnected: function(resolver) {
21759                 return resolver.transient(this, 'isConnected', function() {
21760                     var parents = resolver.parentWays(this);
21761
21762                     if (parents.length > 1) {
21763                         // vertex is connected to multiple parent ways
21764                         for (var i in parents) {
21765                             if (parents[i].geometry(resolver) === 'line' &&
21766                                 parents[i].hasInterestingTags()) { return true; }
21767                         }
21768                     } else if (parents.length === 1) {
21769                         var way = parents[0];
21770                         var nodes = way.nodes.slice();
21771                         if (way.isClosed()) { nodes.pop(); }  // ignore connecting node if closed
21772
21773                         // return true if vertex appears multiple times (way is self intersecting)
21774                         return nodes.indexOf(this.id) !== nodes.lastIndexOf(this.id);
21775                     }
21776
21777                     return false;
21778                 });
21779             },
21780
21781
21782             parentIntersectionWays: function(resolver) {
21783                 return resolver.transient(this, 'parentIntersectionWays', function() {
21784                     return resolver.parentWays(this).filter(function(parent) {
21785                         return (parent.tags.highway ||
21786                             parent.tags.waterway ||
21787                             parent.tags.railway ||
21788                             parent.tags.aeroway) &&
21789                             parent.geometry(resolver) === 'line';
21790                     });
21791                 });
21792             },
21793
21794
21795             isIntersection: function(resolver) {
21796                 return this.parentIntersectionWays(resolver).length > 1;
21797             },
21798
21799
21800             isHighwayIntersection: function(resolver) {
21801                 return resolver.transient(this, 'isHighwayIntersection', function() {
21802                     return resolver.parentWays(this).filter(function(parent) {
21803                         return parent.tags.highway && parent.geometry(resolver) === 'line';
21804                     }).length > 1;
21805                 });
21806             },
21807
21808
21809             isOnAddressLine: function(resolver) {
21810                 return resolver.transient(this, 'isOnAddressLine', function() {
21811                     return resolver.parentWays(this).filter(function(parent) {
21812                         return parent.tags.hasOwnProperty('addr:interpolation') &&
21813                             parent.geometry(resolver) === 'line';
21814                     }).length > 0;
21815                 });
21816             },
21817
21818
21819             asJXON: function(changeset_id) {
21820                 var r = {
21821                     node: {
21822                         '@id': this.osmId(),
21823                         '@lon': this.loc[0],
21824                         '@lat': this.loc[1],
21825                         '@version': (this.version || 0),
21826                         tag: Object.keys(this.tags).map(function(k) {
21827                             return { keyAttributes: { k: k, v: this.tags[k] } };
21828                         }, this)
21829                     }
21830                 };
21831                 if (changeset_id) { r.node['@changeset'] = changeset_id; }
21832                 return r;
21833             },
21834
21835
21836             asGeoJSON: function() {
21837                 return {
21838                     type: 'Point',
21839                     coordinates: this.loc
21840                 };
21841             }
21842         });
21843
21844         function actionCircularize(wayId, projection, maxAngle) {
21845             maxAngle = (maxAngle || 20) * Math.PI / 180;
21846
21847
21848             var action = function(graph, t) {
21849                 if (t === null || !isFinite(t)) { t = 1; }
21850                 t = Math.min(Math.max(+t, 0), 1);
21851
21852                 var way = graph.entity(wayId);
21853                 var origNodes = {};
21854
21855                 graph.childNodes(way).forEach(function(node) {
21856                     if (!origNodes[node.id]) { origNodes[node.id] = node; }
21857                 });
21858
21859                 if (!way.isConvex(graph)) {
21860                     graph = action.makeConvex(graph);
21861                 }
21862
21863                 var nodes = utilArrayUniq(graph.childNodes(way));
21864                 var keyNodes = nodes.filter(function(n) { return graph.parentWays(n).length !== 1; });
21865                 var points = nodes.map(function(n) { return projection(n.loc); });
21866                 var keyPoints = keyNodes.map(function(n) { return projection(n.loc); });
21867                 var centroid = (points.length === 2) ? geoVecInterp(points[0], points[1], 0.5) : d3_polygonCentroid(points);
21868                 var radius = d3_median(points, function(p) { return geoVecLength(centroid, p); });
21869                 var sign = d3_polygonArea(points) > 0 ? 1 : -1;
21870                 var ids, i, j, k;
21871
21872                 // we need atleast two key nodes for the algorithm to work
21873                 if (!keyNodes.length) {
21874                     keyNodes = [nodes[0]];
21875                     keyPoints = [points[0]];
21876                 }
21877
21878                 if (keyNodes.length === 1) {
21879                     var index = nodes.indexOf(keyNodes[0]);
21880                     var oppositeIndex = Math.floor((index + nodes.length / 2) % nodes.length);
21881
21882                     keyNodes.push(nodes[oppositeIndex]);
21883                     keyPoints.push(points[oppositeIndex]);
21884                 }
21885
21886                 // key points and nodes are those connected to the ways,
21887                 // they are projected onto the circle, inbetween nodes are moved
21888                 // to constant intervals between key nodes, extra inbetween nodes are
21889                 // added if necessary.
21890                 for (i = 0; i < keyPoints.length; i++) {
21891                     var nextKeyNodeIndex = (i + 1) % keyNodes.length;
21892                     var startNode = keyNodes[i];
21893                     var endNode = keyNodes[nextKeyNodeIndex];
21894                     var startNodeIndex = nodes.indexOf(startNode);
21895                     var endNodeIndex = nodes.indexOf(endNode);
21896                     var numberNewPoints = -1;
21897                     var indexRange = endNodeIndex - startNodeIndex;
21898                     var nearNodes = {};
21899                     var inBetweenNodes = [];
21900                     var startAngle, endAngle, totalAngle, eachAngle;
21901                     var angle, loc, node, origNode;
21902
21903                     if (indexRange < 0) {
21904                         indexRange += nodes.length;
21905                     }
21906
21907                     // position this key node
21908                     var distance = geoVecLength(centroid, keyPoints[i]) || 1e-4;
21909                     keyPoints[i] = [
21910                         centroid[0] + (keyPoints[i][0] - centroid[0]) / distance * radius,
21911                         centroid[1] + (keyPoints[i][1] - centroid[1]) / distance * radius
21912                     ];
21913                     loc = projection.invert(keyPoints[i]);
21914                     node = keyNodes[i];
21915                     origNode = origNodes[node.id];
21916                     node = node.move(geoVecInterp(origNode.loc, loc, t));
21917                     graph = graph.replace(node);
21918
21919                     // figure out the between delta angle we want to match to
21920                     startAngle = Math.atan2(keyPoints[i][1] - centroid[1], keyPoints[i][0] - centroid[0]);
21921                     endAngle = Math.atan2(keyPoints[nextKeyNodeIndex][1] - centroid[1], keyPoints[nextKeyNodeIndex][0] - centroid[0]);
21922                     totalAngle = endAngle - startAngle;
21923
21924                     // detects looping around -pi/pi
21925                     if (totalAngle * sign > 0) {
21926                         totalAngle = -sign * (2 * Math.PI - Math.abs(totalAngle));
21927                     }
21928
21929                     do {
21930                         numberNewPoints++;
21931                         eachAngle = totalAngle / (indexRange + numberNewPoints);
21932                     } while (Math.abs(eachAngle) > maxAngle);
21933
21934
21935                     // move existing nodes
21936                     for (j = 1; j < indexRange; j++) {
21937                         angle = startAngle + j * eachAngle;
21938                         loc = projection.invert([
21939                             centroid[0] + Math.cos(angle) * radius,
21940                             centroid[1] + Math.sin(angle) * radius
21941                         ]);
21942
21943                         node = nodes[(j + startNodeIndex) % nodes.length];
21944                         origNode = origNodes[node.id];
21945                         nearNodes[node.id] = angle;
21946
21947                         node = node.move(geoVecInterp(origNode.loc, loc, t));
21948                         graph = graph.replace(node);
21949                     }
21950
21951                     // add new inbetween nodes if necessary
21952                     for (j = 0; j < numberNewPoints; j++) {
21953                         angle = startAngle + (indexRange + j) * eachAngle;
21954                         loc = projection.invert([
21955                             centroid[0] + Math.cos(angle) * radius,
21956                             centroid[1] + Math.sin(angle) * radius
21957                         ]);
21958
21959                         // choose a nearnode to use as the original
21960                         var min = Infinity;
21961                         for (var nodeId in nearNodes) {
21962                             var nearAngle = nearNodes[nodeId];
21963                             var dist = Math.abs(nearAngle - angle);
21964                             if (dist < min) {
21965                                 dist = min;
21966                                 origNode = origNodes[nodeId];
21967                             }
21968                         }
21969
21970                         node = osmNode({ loc: geoVecInterp(origNode.loc, loc, t) });
21971                         graph = graph.replace(node);
21972
21973                         nodes.splice(endNodeIndex + j, 0, node);
21974                         inBetweenNodes.push(node.id);
21975                     }
21976
21977                     // Check for other ways that share these keyNodes..
21978                     // If keyNodes are adjacent in both ways,
21979                     // we can add inBetween nodes to that shared way too..
21980                     if (indexRange === 1 && inBetweenNodes.length) {
21981                         var startIndex1 = way.nodes.lastIndexOf(startNode.id);
21982                         var endIndex1 = way.nodes.lastIndexOf(endNode.id);
21983                         var wayDirection1 = (endIndex1 - startIndex1);
21984                         if (wayDirection1 < -1) { wayDirection1 = 1; }
21985
21986                         var parentWays = graph.parentWays(keyNodes[i]);
21987                         for (j = 0; j < parentWays.length; j++) {
21988                             var sharedWay = parentWays[j];
21989                             if (sharedWay === way) { continue; }
21990
21991                             if (sharedWay.areAdjacent(startNode.id, endNode.id)) {
21992                                 var startIndex2 = sharedWay.nodes.lastIndexOf(startNode.id);
21993                                 var endIndex2 = sharedWay.nodes.lastIndexOf(endNode.id);
21994                                 var wayDirection2 = (endIndex2 - startIndex2);
21995                                 var insertAt = endIndex2;
21996                                 if (wayDirection2 < -1) { wayDirection2 = 1; }
21997
21998                                 if (wayDirection1 !== wayDirection2) {
21999                                     inBetweenNodes.reverse();
22000                                     insertAt = startIndex2;
22001                                 }
22002                                 for (k = 0; k < inBetweenNodes.length; k++) {
22003                                     sharedWay = sharedWay.addNode(inBetweenNodes[k], insertAt + k);
22004                                 }
22005                                 graph = graph.replace(sharedWay);
22006                             }
22007                         }
22008                     }
22009
22010                 }
22011
22012                 // update the way to have all the new nodes
22013                 ids = nodes.map(function(n) { return n.id; });
22014                 ids.push(ids[0]);
22015
22016                 way = way.update({nodes: ids});
22017                 graph = graph.replace(way);
22018
22019                 return graph;
22020             };
22021
22022
22023             action.makeConvex = function(graph) {
22024                 var way = graph.entity(wayId);
22025                 var nodes = utilArrayUniq(graph.childNodes(way));
22026                 var points = nodes.map(function(n) { return projection(n.loc); });
22027                 var sign = d3_polygonArea(points) > 0 ? 1 : -1;
22028                 var hull = d3_polygonHull(points);
22029                 var i, j;
22030
22031                 // D3 convex hulls go counterclockwise..
22032                 if (sign === -1) {
22033                     nodes.reverse();
22034                     points.reverse();
22035                 }
22036
22037                 for (i = 0; i < hull.length - 1; i++) {
22038                     var startIndex = points.indexOf(hull[i]);
22039                     var endIndex = points.indexOf(hull[i+1]);
22040                     var indexRange = (endIndex - startIndex);
22041
22042                     if (indexRange < 0) {
22043                         indexRange += nodes.length;
22044                     }
22045
22046                     // move interior nodes to the surface of the convex hull..
22047                     for (j = 1; j < indexRange; j++) {
22048                         var point = geoVecInterp(hull[i], hull[i+1], j / indexRange);
22049                         var node = nodes[(j + startIndex) % nodes.length].move(projection.invert(point));
22050                         graph = graph.replace(node);
22051                     }
22052                 }
22053                 return graph;
22054             };
22055
22056
22057             action.disabled = function(graph) {
22058                 if (!graph.entity(wayId).isClosed()) {
22059                     return 'not_closed';
22060                 }
22061
22062                 //disable when already circular
22063                 var way = graph.entity(wayId);
22064                 var nodes = utilArrayUniq(graph.childNodes(way));
22065                 var points = nodes.map(function(n) { return projection(n.loc); });
22066                 var hull = d3_polygonHull(points);
22067                 var epsilonAngle =  Math.PI / 180;
22068                 if (hull.length !== points.length || hull.length < 3){
22069                     return false;
22070                 }
22071                 var centroid = d3_polygonCentroid(points);
22072                 var radius = geoVecLengthSquare(centroid, points[0]);
22073
22074                 // compare distances between centroid and points
22075                 for (var i = 0; i<hull.length; i++){
22076                     var actualPoint = hull[i];
22077                     var actualDist = geoVecLengthSquare(actualPoint, centroid);
22078                     var diff = Math.abs(actualDist - radius);
22079                     //compare distances with epsilon-error (5%)
22080                     if (diff > 0.05*radius) {
22081                         return false;
22082                     }
22083                 }
22084                 
22085                 //check if central angles are smaller than maxAngle
22086                 for (i = 0; i<hull.length; i++){
22087                     actualPoint = hull[i];
22088                     var nextPoint = hull[(i+1)%hull.length];
22089                     var startAngle = Math.atan2(actualPoint[1] - centroid[1], actualPoint[0] - centroid[0]);
22090                     var endAngle = Math.atan2(nextPoint[1] - centroid[1], nextPoint[0] - centroid[0]);
22091                     var angle = endAngle - startAngle;
22092                     if (angle < 0) {
22093                         angle = -angle;
22094                     }
22095                     if (angle > Math.PI){
22096                         angle = (2*Math.PI - angle);
22097                     }
22098          
22099                     if (angle > maxAngle + epsilonAngle) {
22100                         return false;
22101                     }
22102                 }
22103                 return 'already_circular';
22104             };
22105
22106
22107             action.transitionable = true;
22108
22109
22110             return action;
22111         }
22112
22113         // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/DeleteWayAction.as
22114         function actionDeleteWay(wayID) {
22115
22116             function canDeleteNode(node, graph) {
22117                 // don't delete nodes still attached to ways or relations
22118                 if (graph.parentWays(node).length ||
22119                     graph.parentRelations(node).length) { return false; }
22120
22121                 var geometries = osmNodeGeometriesForTags(node.tags);
22122                 // don't delete if this node can be a standalone point
22123                 if (geometries.point) { return false; }
22124                 // delete if this node only be a vertex
22125                 if (geometries.vertex) { return true; }
22126
22127                 // iD doesn't know if this should be a point or vertex,
22128                 // so only delete if there are no interesting tags
22129                 return !node.hasInterestingTags();
22130             }
22131
22132
22133             var action = function(graph) {
22134                 var way = graph.entity(wayID);
22135
22136                 graph.parentRelations(way).forEach(function(parent) {
22137                     parent = parent.removeMembersWithID(wayID);
22138                     graph = graph.replace(parent);
22139
22140                     if (parent.isDegenerate()) {
22141                         graph = actionDeleteRelation(parent.id)(graph);
22142                     }
22143                 });
22144
22145                 (new Set(way.nodes)).forEach(function(nodeID) {
22146                     graph = graph.replace(way.removeNode(nodeID));
22147
22148                     var node = graph.entity(nodeID);
22149                     if (canDeleteNode(node, graph)) {
22150                         graph = graph.remove(node);
22151                     }
22152                 });
22153
22154                 return graph.remove(way);
22155             };
22156
22157
22158             return action;
22159         }
22160
22161         function actionDeleteMultiple(ids) {
22162             var actions = {
22163                 way: actionDeleteWay,
22164                 node: actionDeleteNode,
22165                 relation: actionDeleteRelation
22166             };
22167
22168
22169             var action = function(graph) {
22170                 ids.forEach(function(id) {
22171                     if (graph.hasEntity(id)) { // It may have been deleted aready.
22172                         graph = actions[graph.entity(id).type](id)(graph);
22173                     }
22174                 });
22175
22176                 return graph;
22177             };
22178
22179
22180             return action;
22181         }
22182
22183         // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/DeleteRelationAction.as
22184         function actionDeleteRelation(relationID, allowUntaggedMembers) {
22185
22186             function canDeleteEntity(entity, graph) {
22187                 return !graph.parentWays(entity).length &&
22188                     !graph.parentRelations(entity).length &&
22189                     (!entity.hasInterestingTags() && !allowUntaggedMembers);
22190             }
22191
22192
22193             var action = function(graph) {
22194                 var relation = graph.entity(relationID);
22195
22196                 graph.parentRelations(relation)
22197                     .forEach(function(parent) {
22198                         parent = parent.removeMembersWithID(relationID);
22199                         graph = graph.replace(parent);
22200
22201                         if (parent.isDegenerate()) {
22202                             graph = actionDeleteRelation(parent.id)(graph);
22203                         }
22204                     });
22205
22206                 var memberIDs = utilArrayUniq(relation.members.map(function(m) { return m.id; }));
22207                 memberIDs.forEach(function(memberID) {
22208                     graph = graph.replace(relation.removeMembersWithID(memberID));
22209
22210                     var entity = graph.entity(memberID);
22211                     if (canDeleteEntity(entity, graph)) {
22212                         graph = actionDeleteMultiple([memberID])(graph);
22213                     }
22214                 });
22215
22216                 return graph.remove(relation);
22217             };
22218
22219
22220             return action;
22221         }
22222
22223         // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/DeleteNodeAction.as
22224         function actionDeleteNode(nodeId) {
22225             var action = function(graph) {
22226                 var node = graph.entity(nodeId);
22227
22228                 graph.parentWays(node)
22229                     .forEach(function(parent) {
22230                         parent = parent.removeNode(nodeId);
22231                         graph = graph.replace(parent);
22232
22233                         if (parent.isDegenerate()) {
22234                             graph = actionDeleteWay(parent.id)(graph);
22235                         }
22236                     });
22237
22238                 graph.parentRelations(node)
22239                     .forEach(function(parent) {
22240                         parent = parent.removeMembersWithID(nodeId);
22241                         graph = graph.replace(parent);
22242
22243                         if (parent.isDegenerate()) {
22244                             graph = actionDeleteRelation(parent.id)(graph);
22245                         }
22246                     });
22247
22248                 return graph.remove(node);
22249             };
22250
22251
22252             return action;
22253         }
22254
22255         // Connect the ways at the given nodes.
22256         //
22257         // First choose a node to be the survivor, with preference given
22258         // to an existing (not new) node.
22259         //
22260         // Tags and relation memberships of of non-surviving nodes are merged
22261         // to the survivor.
22262         //
22263         // This is the inverse of `iD.actionDisconnect`.
22264         //
22265         // Reference:
22266         //   https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MergeNodesAction.as
22267         //   https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/actions/MergeNodesAction.java
22268         //
22269         function actionConnect(nodeIDs) {
22270             var action = function(graph) {
22271                 var survivor;
22272                 var node;
22273                 var parents;
22274                 var i, j;
22275
22276                 // Choose a survivor node, prefer an existing (not new) node - #4974
22277                 for (i = 0; i < nodeIDs.length; i++) {
22278                     survivor = graph.entity(nodeIDs[i]);
22279                     if (survivor.version) { break; }  // found one
22280                 }
22281
22282                 // Replace all non-surviving nodes with the survivor and merge tags.
22283                 for (i = 0; i < nodeIDs.length; i++) {
22284                     node = graph.entity(nodeIDs[i]);
22285                     if (node.id === survivor.id) { continue; }
22286
22287                     parents = graph.parentWays(node);
22288                     for (j = 0; j < parents.length; j++) {
22289                         graph = graph.replace(parents[j].replaceNode(node.id, survivor.id));
22290                     }
22291
22292                     parents = graph.parentRelations(node);
22293                     for (j = 0; j < parents.length; j++) {
22294                         graph = graph.replace(parents[j].replaceMember(node, survivor));
22295                     }
22296
22297                     survivor = survivor.mergeTags(node.tags);
22298                     graph = actionDeleteNode(node.id)(graph);
22299                 }
22300
22301                 graph = graph.replace(survivor);
22302
22303                 // find and delete any degenerate ways created by connecting adjacent vertices
22304                 parents = graph.parentWays(survivor);
22305                 for (i = 0; i < parents.length; i++) {
22306                     if (parents[i].isDegenerate()) {
22307                         graph = actionDeleteWay(parents[i].id)(graph);
22308                     }
22309                 }
22310
22311                 return graph;
22312             };
22313
22314
22315             action.disabled = function(graph) {
22316                 var seen = {};
22317                 var restrictionIDs = [];
22318                 var survivor;
22319                 var node, way;
22320                 var relations, relation, role;
22321                 var i, j, k;
22322
22323                 // Choose a survivor node, prefer an existing (not new) node - #4974
22324                 for (i = 0; i < nodeIDs.length; i++) {
22325                     survivor = graph.entity(nodeIDs[i]);
22326                     if (survivor.version) { break; }  // found one
22327                 }
22328
22329                 // 1. disable if the nodes being connected have conflicting relation roles
22330                 for (i = 0; i < nodeIDs.length; i++) {
22331                     node = graph.entity(nodeIDs[i]);
22332                     relations = graph.parentRelations(node);
22333
22334                     for (j = 0; j < relations.length; j++) {
22335                         relation = relations[j];
22336                         role = relation.memberById(node.id).role || '';
22337
22338                         // if this node is a via node in a restriction, remember for later
22339                         if (relation.hasFromViaTo()) {
22340                             restrictionIDs.push(relation.id);
22341                         }
22342
22343                         if (seen[relation.id] !== undefined && seen[relation.id] !== role) {
22344                             return 'relation';
22345                         } else {
22346                             seen[relation.id] = role;
22347                         }
22348                     }
22349                 }
22350
22351                 // gather restrictions for parent ways
22352                 for (i = 0; i < nodeIDs.length; i++) {
22353                     node = graph.entity(nodeIDs[i]);
22354
22355                     var parents = graph.parentWays(node);
22356                     for (j = 0; j < parents.length; j++) {
22357                         var parent = parents[j];
22358                         relations = graph.parentRelations(parent);
22359
22360                         for (k = 0; k < relations.length; k++) {
22361                             relation = relations[k];
22362                             if (relation.hasFromViaTo()) {
22363                                 restrictionIDs.push(relation.id);
22364                             }
22365                         }
22366                     }
22367                 }
22368
22369
22370                 // test restrictions
22371                 restrictionIDs = utilArrayUniq(restrictionIDs);
22372                 for (i = 0; i < restrictionIDs.length; i++) {
22373                     relation = graph.entity(restrictionIDs[i]);
22374                     if (!relation.isComplete(graph)) { continue; }
22375
22376                     var memberWays = relation.members
22377                         .filter(function(m) { return m.type === 'way'; })
22378                         .map(function(m) { return graph.entity(m.id); });
22379
22380                     memberWays = utilArrayUniq(memberWays);
22381                     var f = relation.memberByRole('from');
22382                     var t = relation.memberByRole('to');
22383                     var isUturn = (f.id === t.id);
22384
22385                     // 2a. disable if connection would damage a restriction
22386                     // (a key node is a node at the junction of ways)
22387                     var nodes = { from: [], via: [], to: [], keyfrom: [], keyto: [] };
22388                     for (j = 0; j < relation.members.length; j++) {
22389                         collectNodes(relation.members[j], nodes);
22390                     }
22391
22392                     nodes.keyfrom = utilArrayUniq(nodes.keyfrom.filter(hasDuplicates));
22393                     nodes.keyto = utilArrayUniq(nodes.keyto.filter(hasDuplicates));
22394
22395                     var filter = keyNodeFilter(nodes.keyfrom, nodes.keyto);
22396                     nodes.from = nodes.from.filter(filter);
22397                     nodes.via = nodes.via.filter(filter);
22398                     nodes.to = nodes.to.filter(filter);
22399
22400                     var connectFrom = false;
22401                     var connectVia = false;
22402                     var connectTo = false;
22403                     var connectKeyFrom = false;
22404                     var connectKeyTo = false;
22405
22406                     for (j = 0; j < nodeIDs.length; j++) {
22407                         var n = nodeIDs[j];
22408                         if (nodes.from.indexOf(n) !== -1)    { connectFrom = true; }
22409                         if (nodes.via.indexOf(n) !== -1)     { connectVia = true; }
22410                         if (nodes.to.indexOf(n) !== -1)      { connectTo = true; }
22411                         if (nodes.keyfrom.indexOf(n) !== -1) { connectKeyFrom = true; }
22412                         if (nodes.keyto.indexOf(n) !== -1)   { connectKeyTo = true; }
22413                     }
22414                     if (connectFrom && connectTo && !isUturn) { return 'restriction'; }
22415                     if (connectFrom && connectVia) { return 'restriction'; }
22416                     if (connectTo   && connectVia) { return 'restriction'; }
22417
22418                     // connecting to a key node -
22419                     // if both nodes are on a member way (i.e. part of the turn restriction),
22420                     // the connecting node must be adjacent to the key node.
22421                     if (connectKeyFrom || connectKeyTo) {
22422                         if (nodeIDs.length !== 2) { return 'restriction'; }
22423
22424                         var n0 = null;
22425                         var n1 = null;
22426                         for (j = 0; j < memberWays.length; j++) {
22427                             way = memberWays[j];
22428                             if (way.contains(nodeIDs[0])) { n0 = nodeIDs[0]; }
22429                             if (way.contains(nodeIDs[1])) { n1 = nodeIDs[1]; }
22430                         }
22431
22432                         if (n0 && n1) {    // both nodes are part of the restriction
22433                             var ok = false;
22434                             for (j = 0; j < memberWays.length; j++) {
22435                                 way = memberWays[j];
22436                                 if (way.areAdjacent(n0, n1)) {
22437                                     ok = true;
22438                                     break;
22439                                 }
22440                             }
22441                             if (!ok) {
22442                                 return 'restriction';
22443                             }
22444                         }
22445                     }
22446
22447                     // 2b. disable if nodes being connected will destroy a member way in a restriction
22448                     // (to test, make a copy and try actually connecting the nodes)
22449                     for (j = 0; j < memberWays.length; j++) {
22450                         way = memberWays[j].update({});   // make copy
22451                         for (k = 0; k < nodeIDs.length; k++) {
22452                             if (nodeIDs[k] === survivor.id) { continue; }
22453
22454                             if (way.areAdjacent(nodeIDs[k], survivor.id)) {
22455                                 way = way.removeNode(nodeIDs[k]);
22456                             } else {
22457                                 way = way.replaceNode(nodeIDs[k], survivor.id);
22458                             }
22459                         }
22460                         if (way.isDegenerate()) {
22461                             return 'restriction';
22462                         }
22463                     }
22464                 }
22465
22466                 return false;
22467
22468
22469                 // if a key node appears multiple times (indexOf !== lastIndexOf) it's a FROM-VIA or TO-VIA junction
22470                 function hasDuplicates(n, i, arr) {
22471                     return arr.indexOf(n) !== arr.lastIndexOf(n);
22472                 }
22473
22474                 function keyNodeFilter(froms, tos) {
22475                     return function(n) {
22476                         return froms.indexOf(n) === -1 && tos.indexOf(n) === -1;
22477                     };
22478                 }
22479
22480                 function collectNodes(member, collection) {
22481                     var entity = graph.hasEntity(member.id);
22482                     if (!entity) { return; }
22483
22484                     var role = member.role || '';
22485                     if (!collection[role]) {
22486                         collection[role] = [];
22487                     }
22488
22489                     if (member.type === 'node') {
22490                         collection[role].push(member.id);
22491                         if (role === 'via') {
22492                             collection.keyfrom.push(member.id);
22493                             collection.keyto.push(member.id);
22494                         }
22495
22496                     } else if (member.type === 'way') {
22497                         collection[role].push.apply(collection[role], entity.nodes);
22498                         if (role === 'from' || role === 'via') {
22499                             collection.keyfrom.push(entity.first());
22500                             collection.keyfrom.push(entity.last());
22501                         }
22502                         if (role === 'to' || role === 'via') {
22503                             collection.keyto.push(entity.first());
22504                             collection.keyto.push(entity.last());
22505                         }
22506                     }
22507                 }
22508             };
22509
22510
22511             return action;
22512         }
22513
22514         function actionCopyEntities(ids, fromGraph) {
22515             var _copies = {};
22516
22517
22518             var action = function(graph) {
22519                 ids.forEach(function(id) {
22520                     fromGraph.entity(id).copy(fromGraph, _copies);
22521                 });
22522
22523                 for (var id in _copies) {
22524                     graph = graph.replace(_copies[id]);
22525                 }
22526
22527                 return graph;
22528             };
22529
22530
22531             action.copies = function() {
22532                 return _copies;
22533             };
22534
22535
22536             return action;
22537         }
22538
22539         function actionDeleteMember(relationId, memberIndex) {
22540             return function(graph) {
22541                 var relation = graph.entity(relationId)
22542                     .removeMember(memberIndex);
22543
22544                 graph = graph.replace(relation);
22545
22546                 if (relation.isDegenerate())
22547                     { graph = actionDeleteRelation(relation.id)(graph); }
22548
22549                 return graph;
22550             };
22551         }
22552
22553         function actionDiscardTags(difference, discardTags) {
22554           discardTags = discardTags || {};
22555
22556           return function (graph) {
22557             difference.modified().forEach(checkTags);
22558             difference.created().forEach(checkTags);
22559             return graph;
22560
22561             function checkTags(entity) {
22562               var keys = Object.keys(entity.tags);
22563               var didDiscard = false;
22564               var tags = {};
22565
22566               for (var i = 0; i < keys.length; i++) {
22567                 var k = keys[i];
22568                 if (discardTags[k] || !entity.tags[k]) {
22569                   didDiscard = true;
22570                 } else {
22571                   tags[k] = entity.tags[k];
22572                 }
22573               }
22574               if (didDiscard) {
22575                 graph = graph.replace(entity.update({ tags: tags }));
22576               }
22577             }
22578
22579           };
22580         }
22581
22582         // Disconect the ways at the given node.
22583         //
22584         // Optionally, disconnect only the given ways.
22585         //
22586         // For testing convenience, accepts an ID to assign to the (first) new node.
22587         // Normally, this will be undefined and the way will automatically
22588         // be assigned a new ID.
22589         //
22590         // This is the inverse of `iD.actionConnect`.
22591         //
22592         // Reference:
22593         //   https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/UnjoinNodeAction.as
22594         //   https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/actions/UnGlueAction.java
22595         //
22596         function actionDisconnect(nodeId, newNodeId) {
22597             var wayIds;
22598
22599
22600             var action = function(graph) {
22601                 var node = graph.entity(nodeId);
22602                 var connections = action.connections(graph);
22603
22604                 connections.forEach(function(connection) {
22605                     var way = graph.entity(connection.wayID);
22606                     var newNode = osmNode({id: newNodeId, loc: node.loc, tags: node.tags});
22607
22608                     graph = graph.replace(newNode);
22609                     if (connection.index === 0 && way.isArea()) {
22610                         // replace shared node with shared node..
22611                         graph = graph.replace(way.replaceNode(way.nodes[0], newNode.id));
22612                     } else if (way.isClosed() && connection.index === way.nodes.length - 1) {
22613                         // replace closing node with new new node..
22614                         graph = graph.replace(way.unclose().addNode(newNode.id));
22615                     } else {
22616                         // replace shared node with multiple new nodes..
22617                         graph = graph.replace(way.updateNode(newNode.id, connection.index));
22618                     }
22619                 });
22620
22621                 return graph;
22622             };
22623
22624
22625             action.connections = function(graph) {
22626                 var candidates = [];
22627                 var keeping = false;
22628                 var parentWays = graph.parentWays(graph.entity(nodeId));
22629                 var way, waynode;
22630                 for (var i = 0; i < parentWays.length; i++) {
22631                     way = parentWays[i];
22632                     if (wayIds && wayIds.indexOf(way.id) === -1) {
22633                         keeping = true;
22634                         continue;
22635                     }
22636                     if (way.isArea() && (way.nodes[0] === nodeId)) {
22637                         candidates.push({ wayID: way.id, index: 0 });
22638                     } else {
22639                         for (var j = 0; j < way.nodes.length; j++) {
22640                             waynode = way.nodes[j];
22641                             if (waynode === nodeId) {
22642                                 if (way.isClosed() &&
22643                                     parentWays.length > 1 &&
22644                                     wayIds &&
22645                                     wayIds.indexOf(way.id) !== -1 &&
22646                                     j === way.nodes.length - 1) {
22647                                     continue;
22648                                 }
22649                                 candidates.push({ wayID: way.id, index: j });
22650                             }
22651                         }
22652                     }
22653                 }
22654
22655                 return keeping ? candidates : candidates.slice(1);
22656             };
22657
22658
22659             action.disabled = function(graph) {
22660                 var connections = action.connections(graph);
22661                 if (connections.length === 0)
22662                     { return 'not_connected'; }
22663
22664                 var parentWays = graph.parentWays(graph.entity(nodeId));
22665                 var seenRelationIds = {};
22666                 var sharedRelation;
22667
22668                 parentWays.forEach(function(way) {
22669                     var relations = graph.parentRelations(way);
22670                     relations.forEach(function(relation) {
22671                         if (relation.id in seenRelationIds) {
22672                             if (wayIds) {
22673                                 if (wayIds.indexOf(way.id) !== -1 ||
22674                                     wayIds.indexOf(seenRelationIds[relation.id]) !== -1) {
22675                                     sharedRelation = relation;
22676                                 }
22677                             } else {
22678                                 sharedRelation = relation;
22679                             }
22680                         } else {
22681                             seenRelationIds[relation.id] = way.id;
22682                         }
22683                     });
22684                 });
22685
22686                 if (sharedRelation)
22687                     { return 'relation'; }
22688             };
22689
22690
22691             action.limitWays = function(val) {
22692                 if (!arguments.length) { return wayIds; }
22693                 wayIds = val;
22694                 return action;
22695             };
22696
22697
22698             return action;
22699         }
22700
22701         function actionExtract(entityID) {
22702
22703             var extractedNodeID;
22704
22705             var action = function(graph) {
22706                 var entity = graph.entity(entityID);
22707
22708                 if (entity.type === 'node') {
22709                     return extractFromNode(entity, graph);
22710                 }
22711
22712                 return extractFromWayOrRelation(entity, graph);
22713             };
22714
22715             function extractFromNode(node, graph) {
22716
22717                 extractedNodeID = node.id;
22718
22719                 // Create a new node to replace the one we will detach
22720                 var replacement = osmNode({ loc: node.loc });
22721                 graph = graph.replace(replacement);
22722
22723                 // Process each way in turn, updating the graph as we go
22724                 graph = graph.parentWays(node)
22725                     .reduce(function(accGraph, parentWay) {
22726                         return accGraph.replace(parentWay.replaceNode(entityID, replacement.id));
22727                     }, graph);
22728
22729                 // Process any relations too
22730                 return graph.parentRelations(node)
22731                     .reduce(function(accGraph, parentRel) {
22732                         return accGraph.replace(parentRel.replaceMember(node, replacement));
22733                     }, graph);
22734             }
22735
22736             function extractFromWayOrRelation(entity, graph) {
22737
22738                 var fromGeometry = entity.geometry(graph);
22739
22740                 var keysToCopyAndRetain = ['source', 'wheelchair'];
22741                 var keysToRetain = ['area'];
22742                 var buildingKeysToRetain = ['architect', 'building', 'height', 'layer'];
22743
22744                 var extractedLoc = d3_geoCentroid(entity.asGeoJSON(graph));
22745                 if (!extractedLoc  || !isFinite(extractedLoc[0]) || !isFinite(extractedLoc[1])) {
22746                     extractedLoc = entity.extent(graph).center();
22747                 }
22748
22749                 var isBuilding = entity.tags.building && entity.tags.building !== 'no';
22750
22751                 var entityTags = Object.assign({}, entity.tags);  // shallow copy
22752                 var pointTags = {};
22753                 for (var key in entityTags) {
22754
22755                     if (entity.type === 'relation' &&
22756                         key === 'type') {
22757                         continue;
22758                     }
22759
22760                     if (keysToRetain.indexOf(key) !== -1) {
22761                         continue;
22762                     }
22763
22764                     if (isBuilding) {
22765                         // don't transfer building-related tags
22766                         if (buildingKeysToRetain.indexOf(key) !== -1 ||
22767                             key.match(/^building:.{1,}/) ||
22768                             key.match(/^roof:.{1,}/)) { continue; }
22769                     }
22770
22771                     // copy the tag from the entity to the point
22772                     pointTags[key] = entityTags[key];
22773
22774                     // leave addresses and some other tags so they're on both features
22775                     if (keysToCopyAndRetain.indexOf(key) !== -1 ||
22776                         key.match(/^addr:.{1,}/)) {
22777                         continue;
22778                     }
22779
22780                     // remove the tag from the entity
22781                     delete entityTags[key];
22782                 }
22783
22784                 if (!isBuilding && fromGeometry === 'area') {
22785                     // ensure that areas keep area geometry
22786                     entityTags.area = 'yes';
22787                 }
22788
22789                 var replacement = osmNode({ loc: extractedLoc, tags: pointTags });
22790                 graph = graph.replace(replacement);
22791
22792                 extractedNodeID = replacement.id;
22793
22794                 return graph.replace(entity.update({tags: entityTags}));
22795             }
22796
22797             action.getExtractedNodeID = function() {
22798                 return extractedNodeID;
22799             };
22800
22801             return action;
22802         }
22803
22804         // Join ways at the end node they share.
22805         //
22806         // This is the inverse of `iD.actionSplit`.
22807         //
22808         // Reference:
22809         //   https://github.com/systemed/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MergeWaysAction.as
22810         //   https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/actions/CombineWayAction.java
22811         //
22812         function actionJoin(ids) {
22813
22814             function groupEntitiesByGeometry(graph) {
22815                 var entities = ids.map(function(id) { return graph.entity(id); });
22816                 return Object.assign(
22817                     { line: [] },
22818                     utilArrayGroupBy(entities, function(entity) { return entity.geometry(graph); })
22819                 );
22820             }
22821
22822
22823             var action = function(graph) {
22824                 var ways = ids.map(graph.entity, graph);
22825                 var survivorID = ways[0].id;
22826
22827                 // if any of the ways are sided (e.g. coastline, cliff, kerb)
22828                 // sort them first so they establish the overall order - #6033
22829                 ways.sort(function(a, b) {
22830                     var aSided = a.isSided();
22831                     var bSided = b.isSided();
22832                     return (aSided && !bSided) ? -1
22833                         : (bSided && !aSided) ? 1
22834                         : 0;
22835                 });
22836
22837                 // Prefer to keep an existing way.
22838                 for (var i = 0; i < ways.length; i++) {
22839                     if (!ways[i].isNew()) {
22840                         survivorID = ways[i].id;
22841                         break;
22842                     }
22843                 }
22844
22845                 var sequences = osmJoinWays(ways, graph);
22846                 var joined = sequences[0];
22847
22848                 // We might need to reverse some of these ways before joining them.  #4688
22849                 // `joined.actions` property will contain any actions we need to apply.
22850                 graph = sequences.actions.reduce(function(g, action) { return action(g); }, graph);
22851
22852                 var survivor = graph.entity(survivorID);
22853                 survivor = survivor.update({ nodes: joined.nodes.map(function(n) { return n.id; }) });
22854                 graph = graph.replace(survivor);
22855
22856                 joined.forEach(function(way) {
22857                     if (way.id === survivorID) { return; }
22858
22859                     graph.parentRelations(way).forEach(function(parent) {
22860                         graph = graph.replace(parent.replaceMember(way, survivor));
22861                     });
22862
22863                     survivor = survivor.mergeTags(way.tags);
22864
22865                     graph = graph.replace(survivor);
22866                     graph = actionDeleteWay(way.id)(graph);
22867                 });
22868
22869                 // Finds if the join created a single-member multipolygon,
22870                 // and if so turns it into a basic area instead
22871                 function checkForSimpleMultipolygon() {
22872                     if (!survivor.isClosed()) { return; }
22873
22874                     var multipolygons = graph.parentMultipolygons(survivor).filter(function(multipolygon) {
22875                         // find multipolygons where the survivor is the only member
22876                         return multipolygon.members.length === 1;
22877                     });
22878
22879                     // skip if this is the single member of multiple multipolygons
22880                     if (multipolygons.length !== 1) { return; }
22881
22882                     var multipolygon = multipolygons[0];
22883
22884                     for (var key in survivor.tags) {
22885                         if (multipolygon.tags[key] &&
22886                             // don't collapse if tags cannot be cleanly merged
22887                             multipolygon.tags[key] !== survivor.tags[key]) { return; }
22888                     }
22889
22890                     survivor = survivor.mergeTags(multipolygon.tags);
22891                     graph = graph.replace(survivor);
22892                     graph = actionDeleteRelation(multipolygon.id, true /* allow untagged members */)(graph);
22893
22894                     var tags = Object.assign({}, survivor.tags);
22895                     if (survivor.geometry(graph) !== 'area') {
22896                         // ensure the feature persists as an area
22897                         tags.area = 'yes';
22898                     }
22899                     delete tags.type; // remove type=multipolygon
22900                     survivor = survivor.update({ tags: tags });
22901                     graph = graph.replace(survivor);
22902                 }
22903                 checkForSimpleMultipolygon();
22904
22905                 return graph;
22906             };
22907
22908             // Returns the number of nodes the resultant way is expected to have
22909             action.resultingWayNodesLength = function(graph) {
22910                 return ids.reduce(function(count, id) {
22911                     return count + graph.entity(id).nodes.length;
22912                 }, 0) - ids.length - 1;
22913             };
22914
22915
22916             action.disabled = function(graph) {
22917                 var geometries = groupEntitiesByGeometry(graph);
22918                 if (ids.length < 2 || ids.length !== geometries.line.length) {
22919                     return 'not_eligible';
22920                 }
22921
22922                 var joined = osmJoinWays(ids.map(graph.entity, graph), graph);
22923                 if (joined.length > 1) {
22924                     return 'not_adjacent';
22925                 }
22926
22927                 // Loop through all combinations of path-pairs
22928                 // to check potential intersections between all pairs
22929                 for (var i = 0; i < ids.length - 1; i++) {
22930                     for (var j = i + 1; j < ids.length; j++) {
22931                         var path1 = graph.childNodes(graph.entity(ids[i]))
22932                             .map(function(e) { return e.loc; });
22933                         var path2 = graph.childNodes(graph.entity(ids[j]))
22934                             .map(function(e) { return e.loc; });
22935                         var intersections = geoPathIntersections(path1, path2);
22936
22937                         // Check if intersections are just nodes lying on top of
22938                         // each other/the line, as opposed to crossing it
22939                         var common = utilArrayIntersection(
22940                             joined[0].nodes.map(function(n) { return n.loc.toString(); }),
22941                             intersections.map(function(n) { return n.toString(); })
22942                         );
22943                         if (common.length !== intersections.length) {
22944                             return 'paths_intersect';
22945                         }
22946                     }
22947                 }
22948
22949                 var nodeIds = joined[0].nodes.map(function(n) { return n.id; }).slice(1, -1);
22950                 var relation;
22951                 var tags = {};
22952                 var conflicting = false;
22953
22954                 joined[0].forEach(function(way) {
22955                     var parents = graph.parentRelations(way);
22956                     parents.forEach(function(parent) {
22957                         if (parent.isRestriction() && parent.members.some(function(m) { return nodeIds.indexOf(m.id) >= 0; })) {
22958                             relation = parent;
22959                         }
22960                     });
22961
22962                     for (var k in way.tags) {
22963                         if (!(k in tags)) {
22964                             tags[k] = way.tags[k];
22965                         } else if (tags[k] && osmIsInterestingTag(k) && tags[k] !== way.tags[k]) {
22966                             conflicting = true;
22967                         }
22968                     }
22969                 });
22970
22971                 if (relation) {
22972                     return 'restriction';
22973                 }
22974
22975                 if (conflicting) {
22976                     return 'conflicting_tags';
22977                 }
22978             };
22979
22980
22981             return action;
22982         }
22983
22984         function actionMerge(ids) {
22985
22986             function groupEntitiesByGeometry(graph) {
22987                 var entities = ids.map(function(id) { return graph.entity(id); });
22988                 return Object.assign(
22989                     { point: [], area: [], line: [], relation: [] },
22990                     utilArrayGroupBy(entities, function(entity) { return entity.geometry(graph); })
22991                 );
22992             }
22993
22994
22995             var action = function(graph) {
22996                 var geometries = groupEntitiesByGeometry(graph);
22997                 var target = geometries.area[0] || geometries.line[0];
22998                 var points = geometries.point;
22999
23000                 points.forEach(function(point) {
23001                     target = target.mergeTags(point.tags);
23002                     graph = graph.replace(target);
23003
23004                     graph.parentRelations(point).forEach(function(parent) {
23005                         graph = graph.replace(parent.replaceMember(point, target));
23006                     });
23007
23008                     var nodes = utilArrayUniq(graph.childNodes(target));
23009                     var removeNode = point;
23010
23011                     for (var i = 0; i < nodes.length; i++) {
23012                         var node = nodes[i];
23013                         if (graph.parentWays(node).length > 1 ||
23014                             graph.parentRelations(node).length ||
23015                             node.hasInterestingTags()) {
23016                             continue;
23017                         }
23018
23019                         // Found an uninteresting child node on the target way.
23020                         // Move orig point into its place to preserve point's history. #3683
23021                         graph = graph.replace(point.update({ tags: {}, loc: node.loc }));
23022                         target = target.replaceNode(node.id, point.id);
23023                         graph = graph.replace(target);
23024                         removeNode = node;
23025                         break;
23026                     }
23027
23028                     graph = graph.remove(removeNode);
23029                 });
23030
23031                 if (target.tags.area === 'yes') {
23032                     var tags = Object.assign({}, target.tags); // shallow copy
23033                     delete tags.area;
23034                     if (osmTagSuggestingArea(tags)) {
23035                         // remove the `area` tag if area geometry is now implied - #3851
23036                         target = target.update({ tags: tags });
23037                         graph = graph.replace(target);
23038                     }
23039                 }
23040
23041                 return graph;
23042             };
23043
23044
23045             action.disabled = function(graph) {
23046                 var geometries = groupEntitiesByGeometry(graph);
23047                 if (geometries.point.length === 0 ||
23048                     (geometries.area.length + geometries.line.length) !== 1 ||
23049                     geometries.relation.length !== 0) {
23050                     return 'not_eligible';
23051                 }
23052             };
23053
23054
23055             return action;
23056         }
23057
23058         // `actionMergeNodes` is just a combination of:
23059         //
23060         // 1. move all the nodes to a common location
23061         // 2. `actionConnect` them
23062
23063         function actionMergeNodes(nodeIDs, loc) {
23064
23065             // If there is a single "interesting" node, use that as the location.
23066             // Otherwise return the average location of all the nodes.
23067             function chooseLoc(graph) {
23068                 if (!nodeIDs.length) { return null; }
23069                 var sum = [0,0];
23070                 var interestingCount = 0;
23071                 var interestingLoc;
23072
23073                 for (var i = 0; i < nodeIDs.length; i++) {
23074                     var node = graph.entity(nodeIDs[i]);
23075                     if (node.hasInterestingTags()) {
23076                         interestingLoc = (++interestingCount === 1) ? node.loc : null;
23077                     }
23078                     sum = geoVecAdd(sum, node.loc);
23079                 }
23080
23081                 return interestingLoc || geoVecScale(sum, 1 / nodeIDs.length);
23082             }
23083
23084
23085             var action = function(graph) {
23086                 if (nodeIDs.length < 2) { return graph; }
23087                 var toLoc = loc;
23088                 if (!toLoc) {
23089                     toLoc = chooseLoc(graph);
23090                 }
23091
23092                 for (var i = 0; i < nodeIDs.length; i++) {
23093                     var node = graph.entity(nodeIDs[i]);
23094                     if (node.loc !== toLoc) {
23095                         graph = graph.replace(node.move(toLoc));
23096                     }
23097                 }
23098
23099                 return actionConnect(nodeIDs)(graph);
23100             };
23101
23102
23103             action.disabled = function(graph) {
23104                 if (nodeIDs.length < 2) { return 'not_eligible'; }
23105
23106                 for (var i = 0; i < nodeIDs.length; i++) {
23107                     var entity = graph.entity(nodeIDs[i]);
23108                     if (entity.type !== 'node') { return 'not_eligible'; }
23109                 }
23110
23111                 return actionConnect(nodeIDs).disabled(graph);
23112             };
23113
23114             return action;
23115         }
23116
23117         function osmChangeset() {
23118             if (!(this instanceof osmChangeset)) {
23119                 return (new osmChangeset()).initialize(arguments);
23120             } else if (arguments.length) {
23121                 this.initialize(arguments);
23122             }
23123         }
23124
23125
23126         osmEntity.changeset = osmChangeset;
23127
23128         osmChangeset.prototype = Object.create(osmEntity.prototype);
23129
23130         Object.assign(osmChangeset.prototype, {
23131
23132             type: 'changeset',
23133
23134
23135             extent: function() {
23136                 return new geoExtent();
23137             },
23138
23139
23140             geometry: function() {
23141                 return 'changeset';
23142             },
23143
23144
23145             asJXON: function() {
23146                 return {
23147                     osm: {
23148                         changeset: {
23149                             tag: Object.keys(this.tags).map(function(k) {
23150                                 return { '@k': k, '@v': this.tags[k] };
23151                             }, this),
23152                             '@version': 0.6,
23153                             '@generator': 'iD'
23154                         }
23155                     }
23156                 };
23157             },
23158
23159
23160             // Generate [osmChange](http://wiki.openstreetmap.org/wiki/OsmChange)
23161             // XML. Returns a string.
23162             osmChangeJXON: function(changes) {
23163                 var changeset_id = this.id;
23164
23165                 function nest(x, order) {
23166                     var groups = {};
23167                     for (var i = 0; i < x.length; i++) {
23168                         var tagName = Object.keys(x[i])[0];
23169                         if (!groups[tagName]) { groups[tagName] = []; }
23170                         groups[tagName].push(x[i][tagName]);
23171                     }
23172                     var ordered = {};
23173                     order.forEach(function(o) {
23174                         if (groups[o]) { ordered[o] = groups[o]; }
23175                     });
23176                     return ordered;
23177                 }
23178
23179
23180                 // sort relations in a changeset by dependencies
23181                 function sort(changes) {
23182
23183                     // find a referenced relation in the current changeset
23184                     function resolve(item) {
23185                         return relations.find(function(relation) {
23186                             return item.keyAttributes.type === 'relation'
23187                                 && item.keyAttributes.ref === relation['@id'];
23188                         });
23189                     }
23190
23191                     // a new item is an item that has not been already processed
23192                     function isNew(item) {
23193                         return !sorted[ item['@id'] ] && !processing.find(function(proc) {
23194                             return proc['@id'] === item['@id'];
23195                         });
23196                     }
23197
23198                     var processing = [];
23199                     var sorted = {};
23200                     var relations = changes.relation;
23201
23202                     if (!relations) { return changes; }
23203
23204                     for (var i = 0; i < relations.length; i++) {
23205                         var relation = relations[i];
23206
23207                         // skip relation if already sorted
23208                         if (!sorted[relation['@id']]) {
23209                             processing.push(relation);
23210                         }
23211
23212                         while (processing.length > 0) {
23213                             var next = processing[0],
23214                             deps = next.member.map(resolve).filter(Boolean).filter(isNew);
23215                             if (deps.length === 0) {
23216                                 sorted[next['@id']] = next;
23217                                 processing.shift();
23218                             } else {
23219                                 processing = deps.concat(processing);
23220                             }
23221                         }
23222                     }
23223
23224                     changes.relation = Object.values(sorted);
23225                     return changes;
23226                 }
23227
23228                 function rep(entity) {
23229                     return entity.asJXON(changeset_id);
23230                 }
23231
23232                 return {
23233                     osmChange: {
23234                         '@version': 0.6,
23235                         '@generator': 'iD',
23236                         'create': sort(nest(changes.created.map(rep), ['node', 'way', 'relation'])),
23237                         'modify': nest(changes.modified.map(rep), ['node', 'way', 'relation']),
23238                         'delete': Object.assign(nest(changes.deleted.map(rep), ['relation', 'way', 'node']), { '@if-unused': true })
23239                     }
23240                 };
23241             },
23242
23243
23244             asGeoJSON: function() {
23245                 return {};
23246             }
23247
23248         });
23249
23250         function osmNote() {
23251             if (!(this instanceof osmNote)) {
23252                 return (new osmNote()).initialize(arguments);
23253             } else if (arguments.length) {
23254                 this.initialize(arguments);
23255             }
23256         }
23257
23258
23259         osmNote.id = function() {
23260             return osmNote.id.next--;
23261         };
23262
23263
23264         osmNote.id.next = -1;
23265
23266
23267         Object.assign(osmNote.prototype, {
23268
23269             type: 'note',
23270
23271             initialize: function(sources) {
23272                 for (var i = 0; i < sources.length; ++i) {
23273                     var source = sources[i];
23274                     for (var prop in source) {
23275                         if (Object.prototype.hasOwnProperty.call(source, prop)) {
23276                             if (source[prop] === undefined) {
23277                                 delete this[prop];
23278                             } else {
23279                                 this[prop] = source[prop];
23280                             }
23281                         }
23282                     }
23283                 }
23284
23285                 if (!this.id) {
23286                     this.id = osmNote.id() + '';  // as string
23287                 }
23288
23289                 return this;
23290             },
23291
23292             extent: function() {
23293                 return new geoExtent(this.loc);
23294             },
23295
23296             update: function(attrs) {
23297                 return osmNote(this, attrs); // {v: 1 + (this.v || 0)}
23298             },
23299
23300             isNew: function() {
23301                 return this.id < 0;
23302             },
23303
23304             move: function(loc) {
23305                 return this.update({ loc: loc });
23306             }
23307
23308         });
23309
23310         function osmRelation() {
23311             if (!(this instanceof osmRelation)) {
23312                 return (new osmRelation()).initialize(arguments);
23313             } else if (arguments.length) {
23314                 this.initialize(arguments);
23315             }
23316         }
23317
23318
23319         osmEntity.relation = osmRelation;
23320
23321         osmRelation.prototype = Object.create(osmEntity.prototype);
23322
23323
23324         osmRelation.creationOrder = function(a, b) {
23325             var aId = parseInt(osmEntity.id.toOSM(a.id), 10);
23326             var bId = parseInt(osmEntity.id.toOSM(b.id), 10);
23327
23328             if (aId < 0 || bId < 0) { return aId - bId; }
23329             return bId - aId;
23330         };
23331
23332
23333         Object.assign(osmRelation.prototype, {
23334             type: 'relation',
23335             members: [],
23336
23337
23338             copy: function(resolver, copies) {
23339                 if (copies[this.id]) { return copies[this.id]; }
23340
23341                 var copy = osmEntity.prototype.copy.call(this, resolver, copies);
23342
23343                 var members = this.members.map(function(member) {
23344                     return Object.assign({}, member, { id: resolver.entity(member.id).copy(resolver, copies).id });
23345                 });
23346
23347                 copy = copy.update({members: members});
23348                 copies[this.id] = copy;
23349
23350                 return copy;
23351             },
23352
23353
23354             extent: function(resolver, memo) {
23355                 return resolver.transient(this, 'extent', function() {
23356                     if (memo && memo[this.id]) { return geoExtent(); }
23357                     memo = memo || {};
23358                     memo[this.id] = true;
23359
23360                     var extent = geoExtent();
23361                     for (var i = 0; i < this.members.length; i++) {
23362                         var member = resolver.hasEntity(this.members[i].id);
23363                         if (member) {
23364                             extent._extend(member.extent(resolver, memo));
23365                         }
23366                     }
23367                     return extent;
23368                 });
23369             },
23370
23371
23372             geometry: function(graph) {
23373                 return graph.transient(this, 'geometry', function() {
23374                     return this.isMultipolygon() ? 'area' : 'relation';
23375                 });
23376             },
23377
23378
23379             isDegenerate: function() {
23380                 return this.members.length === 0;
23381             },
23382
23383
23384             // Return an array of members, each extended with an 'index' property whose value
23385             // is the member index.
23386             indexedMembers: function() {
23387                 var result = new Array(this.members.length);
23388                 for (var i = 0; i < this.members.length; i++) {
23389                     result[i] = Object.assign({}, this.members[i], {index: i});
23390                 }
23391                 return result;
23392             },
23393
23394
23395             // Return the first member with the given role. A copy of the member object
23396             // is returned, extended with an 'index' property whose value is the member index.
23397             memberByRole: function(role) {
23398                 for (var i = 0; i < this.members.length; i++) {
23399                     if (this.members[i].role === role) {
23400                         return Object.assign({}, this.members[i], {index: i});
23401                     }
23402                 }
23403             },
23404
23405             // Same as memberByRole, but returns all members with the given role
23406             membersByRole: function(role) {
23407                 var result = [];
23408                 for (var i = 0; i < this.members.length; i++) {
23409                     if (this.members[i].role === role) {
23410                         result.push(Object.assign({}, this.members[i], {index: i}));
23411                     }
23412                 }
23413                 return result;
23414             },
23415
23416             // Return the first member with the given id. A copy of the member object
23417             // is returned, extended with an 'index' property whose value is the member index.
23418             memberById: function(id) {
23419                 for (var i = 0; i < this.members.length; i++) {
23420                     if (this.members[i].id === id) {
23421                         return Object.assign({}, this.members[i], {index: i});
23422                     }
23423                 }
23424             },
23425
23426
23427             // Return the first member with the given id and role. A copy of the member object
23428             // is returned, extended with an 'index' property whose value is the member index.
23429             memberByIdAndRole: function(id, role) {
23430                 for (var i = 0; i < this.members.length; i++) {
23431                     if (this.members[i].id === id && this.members[i].role === role) {
23432                         return Object.assign({}, this.members[i], {index: i});
23433                     }
23434                 }
23435             },
23436
23437
23438             addMember: function(member, index) {
23439                 var members = this.members.slice();
23440                 members.splice(index === undefined ? members.length : index, 0, member);
23441                 return this.update({members: members});
23442             },
23443
23444
23445             updateMember: function(member, index) {
23446                 var members = this.members.slice();
23447                 members.splice(index, 1, Object.assign({}, members[index], member));
23448                 return this.update({members: members});
23449             },
23450
23451
23452             removeMember: function(index) {
23453                 var members = this.members.slice();
23454                 members.splice(index, 1);
23455                 return this.update({members: members});
23456             },
23457
23458
23459             removeMembersWithID: function(id) {
23460                 var members = this.members.filter(function(m) { return m.id !== id; });
23461                 return this.update({members: members});
23462             },
23463
23464             moveMember: function(fromIndex, toIndex) {
23465                 var members = this.members.slice();
23466                 members.splice(toIndex, 0, members.splice(fromIndex, 1)[0]);
23467                 return this.update({members: members});
23468             },
23469
23470
23471             // Wherever a member appears with id `needle.id`, replace it with a member
23472             // with id `replacement.id`, type `replacement.type`, and the original role,
23473             // By default, adding a duplicate member (by id and role) is prevented.
23474             // Return an updated relation.
23475             replaceMember: function(needle, replacement, keepDuplicates) {
23476                 if (!this.memberById(needle.id)) { return this; }
23477
23478                 var members = [];
23479
23480                 for (var i = 0; i < this.members.length; i++) {
23481                     var member = this.members[i];
23482                     if (member.id !== needle.id) {
23483                         members.push(member);
23484                     } else if (keepDuplicates || !this.memberByIdAndRole(replacement.id, member.role)) {
23485                         members.push({ id: replacement.id, type: replacement.type, role: member.role });
23486                     }
23487                 }
23488
23489                 return this.update({ members: members });
23490             },
23491
23492
23493             asJXON: function(changeset_id) {
23494                 var r = {
23495                     relation: {
23496                         '@id': this.osmId(),
23497                         '@version': this.version || 0,
23498                         member: this.members.map(function(member) {
23499                             return {
23500                                 keyAttributes: {
23501                                     type: member.type,
23502                                     role: member.role,
23503                                     ref: osmEntity.id.toOSM(member.id)
23504                                 }
23505                             };
23506                         }, this),
23507                         tag: Object.keys(this.tags).map(function(k) {
23508                             return { keyAttributes: { k: k, v: this.tags[k] } };
23509                         }, this)
23510                     }
23511                 };
23512                 if (changeset_id) {
23513                     r.relation['@changeset'] = changeset_id;
23514                 }
23515                 return r;
23516             },
23517
23518
23519             asGeoJSON: function(resolver) {
23520                 return resolver.transient(this, 'GeoJSON', function () {
23521                     if (this.isMultipolygon()) {
23522                         return {
23523                             type: 'MultiPolygon',
23524                             coordinates: this.multipolygon(resolver)
23525                         };
23526                     } else {
23527                         return {
23528                             type: 'FeatureCollection',
23529                             properties: this.tags,
23530                             features: this.members.map(function (member) {
23531                                 return Object.assign({role: member.role}, resolver.entity(member.id).asGeoJSON(resolver));
23532                             })
23533                         };
23534                     }
23535                 });
23536             },
23537
23538
23539             area: function(resolver) {
23540                 return resolver.transient(this, 'area', function() {
23541                     return d3_geoArea(this.asGeoJSON(resolver));
23542                 });
23543             },
23544
23545
23546             isMultipolygon: function() {
23547                 return this.tags.type === 'multipolygon';
23548             },
23549
23550
23551             isComplete: function(resolver) {
23552                 for (var i = 0; i < this.members.length; i++) {
23553                     if (!resolver.hasEntity(this.members[i].id)) {
23554                         return false;
23555                     }
23556                 }
23557                 return true;
23558             },
23559
23560
23561             hasFromViaTo: function() {
23562                 return (
23563                     this.members.some(function(m) { return m.role === 'from'; }) &&
23564                     this.members.some(function(m) { return m.role === 'via'; }) &&
23565                     this.members.some(function(m) { return m.role === 'to'; })
23566                 );
23567             },
23568
23569
23570             isRestriction: function() {
23571                 return !!(this.tags.type && this.tags.type.match(/^restriction:?/));
23572             },
23573
23574
23575             isValidRestriction: function() {
23576                 if (!this.isRestriction()) { return false; }
23577
23578                 var froms = this.members.filter(function(m) { return m.role === 'from'; });
23579                 var vias = this.members.filter(function(m) { return m.role === 'via'; });
23580                 var tos = this.members.filter(function(m) { return m.role === 'to'; });
23581
23582                 if (froms.length !== 1 && this.tags.restriction !== 'no_entry') { return false; }
23583                 if (froms.some(function(m) { return m.type !== 'way'; })) { return false; }
23584
23585                 if (tos.length !== 1 && this.tags.restriction !== 'no_exit') { return false; }
23586                 if (tos.some(function(m) { return m.type !== 'way'; })) { return false; }
23587
23588                 if (vias.length === 0) { return false; }
23589                 if (vias.length > 1 && vias.some(function(m) { return m.type !== 'way'; })) { return false; }
23590
23591                 return true;
23592             },
23593
23594
23595             // Returns an array [A0, ... An], each Ai being an array of node arrays [Nds0, ... Ndsm],
23596             // where Nds0 is an outer ring and subsequent Ndsi's (if any i > 0) being inner rings.
23597             //
23598             // This corresponds to the structure needed for rendering a multipolygon path using a
23599             // `evenodd` fill rule, as well as the structure of a GeoJSON MultiPolygon geometry.
23600             //
23601             // In the case of invalid geometries, this function will still return a result which
23602             // includes the nodes of all way members, but some Nds may be unclosed and some inner
23603             // rings not matched with the intended outer ring.
23604             //
23605             multipolygon: function(resolver) {
23606                 var outers = this.members.filter(function(m) { return 'outer' === (m.role || 'outer'); });
23607                 var inners = this.members.filter(function(m) { return 'inner' === m.role; });
23608
23609                 outers = osmJoinWays(outers, resolver);
23610                 inners = osmJoinWays(inners, resolver);
23611
23612                 var sequenceToLineString = function(sequence) {
23613                     if (sequence.nodes.length > 2 &&
23614                         sequence.nodes[0] !== sequence.nodes[sequence.nodes.length - 1]) {
23615                         // close unclosed parts to ensure correct area rendering - #2945
23616                         sequence.nodes.push(sequence.nodes[0]);
23617                     }
23618                     return sequence.nodes.map(function(node) { return node.loc; });
23619                 };
23620
23621                 outers = outers.map(sequenceToLineString);
23622                 inners = inners.map(sequenceToLineString);
23623
23624                 var result = outers.map(function(o) {
23625                     // Heuristic for detecting counterclockwise winding order. Assumes
23626                     // that OpenStreetMap polygons are not hemisphere-spanning.
23627                     return [d3_geoArea({ type: 'Polygon', coordinates: [o] }) > 2 * Math.PI ? o.reverse() : o];
23628                 });
23629
23630                 function findOuter(inner) {
23631                     var o, outer;
23632
23633                     for (o = 0; o < outers.length; o++) {
23634                         outer = outers[o];
23635                         if (geoPolygonContainsPolygon(outer, inner))
23636                             { return o; }
23637                     }
23638
23639                     for (o = 0; o < outers.length; o++) {
23640                         outer = outers[o];
23641                         if (geoPolygonIntersectsPolygon(outer, inner, false))
23642                             { return o; }
23643                     }
23644                 }
23645
23646                 for (var i = 0; i < inners.length; i++) {
23647                     var inner = inners[i];
23648
23649                     if (d3_geoArea({ type: 'Polygon', coordinates: [inner] }) < 2 * Math.PI) {
23650                         inner = inner.reverse();
23651                     }
23652
23653                     var o = findOuter(inners[i]);
23654                     if (o !== undefined) {
23655                         result[o].push(inners[i]);
23656                     } else {
23657                         result.push([inners[i]]); // Invalid geometry
23658                     }
23659                 }
23660
23661                 return result;
23662             }
23663         });
23664
23665         var QAItem = function QAItem(loc, service, itemType, id, props) {
23666           // Store required properties
23667           this.loc = loc;
23668           this.service = service.title;
23669           this.itemType = itemType;
23670
23671           // All issues must have an ID for selection, use generic if none specified
23672           this.id = id ? id : ("" + (QAItem.id()));
23673
23674           this.update(props);
23675
23676           // Some QA services have marker icons to differentiate issues
23677           if (service && typeof service.getIcon === 'function') {
23678             this.icon = service.getIcon(itemType);
23679           }
23680
23681           return this;
23682         };
23683
23684         QAItem.prototype.update = function update (props) {
23685             var this$1 = this;
23686
23687           // You can't override this inital information
23688           var ref = this;
23689             var loc = ref.loc;
23690             var service = ref.service;
23691             var itemType = ref.itemType;
23692             var id = ref.id;
23693
23694           Object.keys(props).forEach(function (prop) { return this$1[prop] = props[prop]; });
23695
23696           this.loc = loc;
23697           this.service = service;
23698           this.itemType = itemType;
23699           this.id = id;
23700
23701           return this;
23702         };
23703
23704         // Generic handling for newly created QAItems
23705         QAItem.id = function id () {
23706           return this.nextId--;
23707         };
23708         QAItem.nextId = -1;
23709
23710         // Split a way at the given node.
23711         //
23712         // Optionally, split only the given ways, if multiple ways share
23713         // the given node.
23714         //
23715         // This is the inverse of `iD.actionJoin`.
23716         //
23717         // For testing convenience, accepts an ID to assign to the new way.
23718         // Normally, this will be undefined and the way will automatically
23719         // be assigned a new ID.
23720         //
23721         // Reference:
23722         //   https://github.com/systemed/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/SplitWayAction.as
23723         //
23724         function actionSplit(nodeId, newWayIds) {
23725             var _wayIDs;
23726
23727             // The IDs of the ways actually created by running this action
23728             var createdWayIDs = [];
23729
23730             // If the way is closed, we need to search for a partner node
23731             // to split the way at.
23732             //
23733             // The following looks for a node that is both far away from
23734             // the initial node in terms of way segment length and nearby
23735             // in terms of beeline-distance. This assures that areas get
23736             // split on the most "natural" points (independent of the number
23737             // of nodes).
23738             // For example: bone-shaped areas get split across their waist
23739             // line, circles across the diameter.
23740             function splitArea(nodes, idxA, graph) {
23741                 var lengths = new Array(nodes.length);
23742                 var length;
23743                 var i;
23744                 var best = 0;
23745                 var idxB;
23746
23747                 function wrap(index) {
23748                     return utilWrap(index, nodes.length);
23749                 }
23750
23751                 function dist(nA, nB) {
23752                     var locA = graph.entity(nA).loc;
23753                     var locB = graph.entity(nB).loc;
23754                     var epsilon = 1e-6;
23755                     return (locA && locB) ? geoSphericalDistance(locA, locB) : epsilon;
23756                 }
23757
23758                 // calculate lengths
23759                 length = 0;
23760                 for (i = wrap(idxA + 1); i !== idxA; i = wrap(i + 1)) {
23761                     length += dist(nodes[i], nodes[wrap(i - 1)]);
23762                     lengths[i] = length;
23763                 }
23764
23765                 length = 0;
23766                 for (i = wrap(idxA - 1); i !== idxA; i = wrap(i - 1)) {
23767                     length += dist(nodes[i], nodes[wrap(i + 1)]);
23768                     if (length < lengths[i]) {
23769                         lengths[i] = length;
23770                     }
23771                 }
23772
23773                 // determine best opposite node to split
23774                 for (i = 0; i < nodes.length; i++) {
23775                     var cost = lengths[i] / dist(nodes[idxA], nodes[i]);
23776                     if (cost > best) {
23777                         idxB = i;
23778                         best = cost;
23779                     }
23780                 }
23781
23782                 return idxB;
23783             }
23784
23785
23786             function split(graph, wayA, newWayId) {
23787                 var wayB = osmWay({ id: newWayId, tags: wayA.tags });   // `wayB` is the NEW way
23788                 var origNodes = wayA.nodes.slice();
23789                 var nodesA;
23790                 var nodesB;
23791                 var isArea = wayA.isArea();
23792                 var isOuter = osmIsOldMultipolygonOuterMember(wayA, graph);
23793
23794                 if (wayA.isClosed()) {
23795                     var nodes = wayA.nodes.slice(0, -1);
23796                     var idxA = nodes.indexOf(nodeId);
23797                     var idxB = splitArea(nodes, idxA, graph);
23798
23799                     if (idxB < idxA) {
23800                         nodesA = nodes.slice(idxA).concat(nodes.slice(0, idxB + 1));
23801                         nodesB = nodes.slice(idxB, idxA + 1);
23802                     } else {
23803                         nodesA = nodes.slice(idxA, idxB + 1);
23804                         nodesB = nodes.slice(idxB).concat(nodes.slice(0, idxA + 1));
23805                     }
23806                 } else {
23807                     var idx = wayA.nodes.indexOf(nodeId, 1);
23808                     nodesA = wayA.nodes.slice(0, idx + 1);
23809                     nodesB = wayA.nodes.slice(idx);
23810                 }
23811
23812                 wayA = wayA.update({ nodes: nodesA });
23813                 wayB = wayB.update({ nodes: nodesB });
23814
23815                 graph = graph.replace(wayA);
23816                 graph = graph.replace(wayB);
23817
23818                 graph.parentRelations(wayA).forEach(function(relation) {
23819                     var member;
23820
23821                     // Turn restrictions - make sure:
23822                     // 1. Splitting a FROM/TO way - only `wayA` OR `wayB` remains in relation
23823                     //    (whichever one is connected to the VIA node/ways)
23824                     // 2. Splitting a VIA way - `wayB` remains in relation as a VIA way
23825                     if (relation.hasFromViaTo()) {
23826                         var f = relation.memberByRole('from');
23827                         var v = relation.membersByRole('via');
23828                         var t = relation.memberByRole('to');
23829                         var i;
23830
23831                         // 1. split a FROM/TO
23832                         if (f.id === wayA.id || t.id === wayA.id) {
23833                             var keepB = false;
23834                             if (v.length === 1 && v[0].type === 'node') {   // check via node
23835                                 keepB = wayB.contains(v[0].id);
23836                             } else {                                        // check via way(s)
23837                                 for (i = 0; i < v.length; i++) {
23838                                     if (v[i].type === 'way') {
23839                                         var wayVia = graph.hasEntity(v[i].id);
23840                                         if (wayVia && utilArrayIntersection(wayB.nodes, wayVia.nodes).length) {
23841                                             keepB = true;
23842                                             break;
23843                                         }
23844                                     }
23845                                 }
23846                             }
23847
23848                             if (keepB) {
23849                                 relation = relation.replaceMember(wayA, wayB);
23850                                 graph = graph.replace(relation);
23851                             }
23852
23853                         // 2. split a VIA
23854                         } else {
23855                             for (i = 0; i < v.length; i++) {
23856                                 if (v[i].type === 'way' && v[i].id === wayA.id) {
23857                                     member = {
23858                                         id: wayB.id,
23859                                         type: 'way',
23860                                         role: 'via'
23861                                     };
23862                                     graph = actionAddMember(relation.id, member, v[i].index + 1)(graph);
23863                                     break;
23864                                 }
23865                             }
23866                         }
23867
23868                     // All other relations (Routes, Multipolygons, etc):
23869                     // 1. Both `wayA` and `wayB` remain in the relation
23870                     // 2. But must be inserted as a pair (see `actionAddMember` for details)
23871                     } else {
23872                         if (relation === isOuter) {
23873                             graph = graph.replace(relation.mergeTags(wayA.tags));
23874                             graph = graph.replace(wayA.update({ tags: {} }));
23875                             graph = graph.replace(wayB.update({ tags: {} }));
23876                         }
23877
23878                         member = {
23879                             id: wayB.id,
23880                             type: 'way',
23881                             role: relation.memberById(wayA.id).role
23882                         };
23883
23884                         var insertPair = {
23885                             originalID: wayA.id,
23886                             insertedID: wayB.id,
23887                             nodes: origNodes
23888                         };
23889
23890                         graph = actionAddMember(relation.id, member, undefined, insertPair)(graph);
23891                     }
23892                 });
23893
23894                 if (!isOuter && isArea) {
23895                     var multipolygon = osmRelation({
23896                         tags: Object.assign({}, wayA.tags, { type: 'multipolygon' }),
23897                         members: [
23898                             { id: wayA.id, role: 'outer', type: 'way' },
23899                             { id: wayB.id, role: 'outer', type: 'way' }
23900                         ]
23901                     });
23902
23903                     graph = graph.replace(multipolygon);
23904                     graph = graph.replace(wayA.update({ tags: {} }));
23905                     graph = graph.replace(wayB.update({ tags: {} }));
23906                 }
23907
23908                 createdWayIDs.push(wayB.id);
23909
23910                 return graph;
23911             }
23912
23913             var action = function(graph) {
23914                 var candidates = action.ways(graph);
23915                 createdWayIDs = [];
23916                 for (var i = 0; i < candidates.length; i++) {
23917                     graph = split(graph, candidates[i], newWayIds && newWayIds[i]);
23918                 }
23919                 return graph;
23920             };
23921
23922             action.getCreatedWayIDs = function() {
23923                 return createdWayIDs;
23924             };
23925
23926             action.ways = function(graph) {
23927                 var node = graph.entity(nodeId);
23928                 var parents = graph.parentWays(node);
23929                 var hasLines = parents.some(function(parent) {
23930                     return parent.geometry(graph) === 'line';
23931                 });
23932
23933                 return parents.filter(function(parent) {
23934                     if (_wayIDs && _wayIDs.indexOf(parent.id) === -1)
23935                         { return false; }
23936
23937                     if (!_wayIDs && hasLines && parent.geometry(graph) !== 'line')
23938                         { return false; }
23939
23940                     if (parent.isClosed()) {
23941                         return true;
23942                     }
23943
23944                     for (var i = 1; i < parent.nodes.length - 1; i++) {
23945                         if (parent.nodes[i] === nodeId) {
23946                             return true;
23947                         }
23948                     }
23949
23950                     return false;
23951                 });
23952             };
23953
23954
23955             action.disabled = function(graph) {
23956                 var candidates = action.ways(graph);
23957                 if (candidates.length === 0 || (_wayIDs && _wayIDs.length !== candidates.length)) {
23958                     return 'not_eligible';
23959                 }
23960             };
23961
23962
23963             action.limitWays = function(val) {
23964                 if (!arguments.length) { return _wayIDs; }
23965                 _wayIDs = val;
23966                 return action;
23967             };
23968
23969
23970             return action;
23971         }
23972
23973         function coreGraph(other, mutable) {
23974             if (!(this instanceof coreGraph)) { return new coreGraph(other, mutable); }
23975
23976             if (other instanceof coreGraph) {
23977                 var base = other.base();
23978                 this.entities = Object.assign(Object.create(base.entities), other.entities);
23979                 this._parentWays = Object.assign(Object.create(base.parentWays), other._parentWays);
23980                 this._parentRels = Object.assign(Object.create(base.parentRels), other._parentRels);
23981
23982             } else {
23983                 this.entities = Object.create({});
23984                 this._parentWays = Object.create({});
23985                 this._parentRels = Object.create({});
23986                 this.rebase(other || [], [this]);
23987             }
23988
23989             this.transients = {};
23990             this._childNodes = {};
23991             this.frozen = !mutable;
23992         }
23993
23994
23995         coreGraph.prototype = {
23996
23997             hasEntity: function(id) {
23998                 return this.entities[id];
23999             },
24000
24001
24002             entity: function(id) {
24003                 var entity = this.entities[id];
24004
24005                 //https://github.com/openstreetmap/iD/issues/3973#issuecomment-307052376
24006                 if (!entity) {
24007                     entity = this.entities.__proto__[id];  // eslint-disable-line no-proto
24008                 }
24009
24010                 if (!entity) {
24011                     throw new Error('entity ' + id + ' not found');
24012                 }
24013                 return entity;
24014             },
24015
24016
24017             geometry: function(id) {
24018                 return this.entity(id).geometry(this);
24019             },
24020
24021
24022             transient: function(entity, key, fn) {
24023                 var id = entity.id;
24024                 var transients = this.transients[id] || (this.transients[id] = {});
24025
24026                 if (transients[key] !== undefined) {
24027                     return transients[key];
24028                 }
24029
24030                 transients[key] = fn.call(entity);
24031
24032                 return transients[key];
24033             },
24034
24035
24036             parentWays: function(entity) {
24037                 var parents = this._parentWays[entity.id];
24038                 var result = [];
24039                 if (parents) {
24040                     parents.forEach(function(id) {
24041                         result.push(this.entity(id));
24042                     }, this);
24043                 }
24044                 return result;
24045             },
24046
24047
24048             isPoi: function(entity) {
24049                 var parents = this._parentWays[entity.id];
24050                 return !parents || parents.size === 0;
24051             },
24052
24053
24054             isShared: function(entity) {
24055                 var parents = this._parentWays[entity.id];
24056                 return parents && parents.size > 1;
24057             },
24058
24059
24060             parentRelations: function(entity) {
24061                 var parents = this._parentRels[entity.id];
24062                 var result = [];
24063                 if (parents) {
24064                     parents.forEach(function(id) {
24065                         result.push(this.entity(id));
24066                     }, this);
24067                 }
24068                 return result;
24069             },
24070
24071             parentMultipolygons: function(entity) {
24072                 return this.parentRelations(entity).filter(function(relation) {
24073                     return relation.isMultipolygon();
24074                 });
24075             },
24076
24077
24078             childNodes: function(entity) {
24079                 if (this._childNodes[entity.id]) { return this._childNodes[entity.id]; }
24080                 if (!entity.nodes) { return []; }
24081
24082                 var nodes = [];
24083                 for (var i = 0; i < entity.nodes.length; i++) {
24084                     nodes[i] = this.entity(entity.nodes[i]);
24085                 }
24086
24087                 this._childNodes[entity.id] = nodes;
24088                 return this._childNodes[entity.id];
24089             },
24090
24091
24092             base: function() {
24093                 return {
24094                     'entities': Object.getPrototypeOf(this.entities),
24095                     'parentWays': Object.getPrototypeOf(this._parentWays),
24096                     'parentRels': Object.getPrototypeOf(this._parentRels)
24097                 };
24098             },
24099
24100
24101             // Unlike other graph methods, rebase mutates in place. This is because it
24102             // is used only during the history operation that merges newly downloaded
24103             // data into each state. To external consumers, it should appear as if the
24104             // graph always contained the newly downloaded data.
24105             rebase: function(entities, stack, force) {
24106                 var base = this.base();
24107                 var i, j, k, id;
24108
24109                 for (i = 0; i < entities.length; i++) {
24110                     var entity = entities[i];
24111
24112                     if (!entity.visible || (!force && base.entities[entity.id]))
24113                         { continue; }
24114
24115                     // Merging data into the base graph
24116                     base.entities[entity.id] = entity;
24117                     this._updateCalculated(undefined, entity, base.parentWays, base.parentRels);
24118
24119                     // Restore provisionally-deleted nodes that are discovered to have an extant parent
24120                     if (entity.type === 'way') {
24121                         for (j = 0; j < entity.nodes.length; j++) {
24122                             id = entity.nodes[j];
24123                             for (k = 1; k < stack.length; k++) {
24124                                 var ents = stack[k].entities;
24125                                 if (ents.hasOwnProperty(id) && ents[id] === undefined) {
24126                                     delete ents[id];
24127                                 }
24128                             }
24129                         }
24130                     }
24131                 }
24132
24133                 for (i = 0; i < stack.length; i++) {
24134                     stack[i]._updateRebased();
24135                 }
24136             },
24137
24138
24139             _updateRebased: function() {
24140                 var base = this.base();
24141
24142                 Object.keys(this._parentWays).forEach(function(child) {
24143                     if (base.parentWays[child]) {
24144                         base.parentWays[child].forEach(function(id) {
24145                             if (!this.entities.hasOwnProperty(id)) {
24146                                 this._parentWays[child].add(id);
24147                             }
24148                         }, this);
24149                     }
24150                 }, this);
24151
24152                 Object.keys(this._parentRels).forEach(function(child) {
24153                     if (base.parentRels[child]) {
24154                         base.parentRels[child].forEach(function(id) {
24155                             if (!this.entities.hasOwnProperty(id)) {
24156                                 this._parentRels[child].add(id);
24157                             }
24158                         }, this);
24159                     }
24160                 }, this);
24161
24162                 this.transients = {};
24163
24164                 // this._childNodes is not updated, under the assumption that
24165                 // ways are always downloaded with their child nodes.
24166             },
24167
24168
24169             // Updates calculated properties (parentWays, parentRels) for the specified change
24170             _updateCalculated: function(oldentity, entity, parentWays, parentRels) {
24171                 parentWays = parentWays || this._parentWays;
24172                 parentRels = parentRels || this._parentRels;
24173
24174                 var type = entity && entity.type || oldentity && oldentity.type;
24175                 var removed, added, i;
24176
24177                 if (type === 'way') {   // Update parentWays
24178                     if (oldentity && entity) {
24179                         removed = utilArrayDifference(oldentity.nodes, entity.nodes);
24180                         added = utilArrayDifference(entity.nodes, oldentity.nodes);
24181                     } else if (oldentity) {
24182                         removed = oldentity.nodes;
24183                         added = [];
24184                     } else if (entity) {
24185                         removed = [];
24186                         added = entity.nodes;
24187                     }
24188                     for (i = 0; i < removed.length; i++) {
24189                         // make a copy of prototype property, store as own property, and update..
24190                         parentWays[removed[i]] = new Set(parentWays[removed[i]]);
24191                         parentWays[removed[i]].delete(oldentity.id);
24192                     }
24193                     for (i = 0; i < added.length; i++) {
24194                         // make a copy of prototype property, store as own property, and update..
24195                         parentWays[added[i]] = new Set(parentWays[added[i]]);
24196                         parentWays[added[i]].add(entity.id);
24197                     }
24198
24199                 } else if (type === 'relation') {   // Update parentRels
24200
24201                     // diff only on the IDs since the same entity can be a member multiple times with different roles
24202                     var oldentityMemberIDs = oldentity ? oldentity.members.map(function(m) { return m.id; }) : [];
24203                     var entityMemberIDs = entity ? entity.members.map(function(m) { return m.id; }) : [];
24204
24205                     if (oldentity && entity) {
24206                         removed = utilArrayDifference(oldentityMemberIDs, entityMemberIDs);
24207                         added = utilArrayDifference(entityMemberIDs, oldentityMemberIDs);
24208                     } else if (oldentity) {
24209                         removed = oldentityMemberIDs;
24210                         added = [];
24211                     } else if (entity) {
24212                         removed = [];
24213                         added = entityMemberIDs;
24214                     }
24215                     for (i = 0; i < removed.length; i++) {
24216                         // make a copy of prototype property, store as own property, and update..
24217                         parentRels[removed[i]] = new Set(parentRels[removed[i]]);
24218                         parentRels[removed[i]].delete(oldentity.id);
24219                     }
24220                     for (i = 0; i < added.length; i++) {
24221                         // make a copy of prototype property, store as own property, and update..
24222                         parentRels[added[i]] = new Set(parentRels[added[i]]);
24223                         parentRels[added[i]].add(entity.id);
24224                     }
24225                 }
24226             },
24227
24228
24229             replace: function(entity) {
24230                 if (this.entities[entity.id] === entity) { return this; }
24231
24232                 return this.update(function() {
24233                     this._updateCalculated(this.entities[entity.id], entity);
24234                     this.entities[entity.id] = entity;
24235                 });
24236             },
24237
24238
24239             remove: function(entity) {
24240                 return this.update(function() {
24241                     this._updateCalculated(entity, undefined);
24242                     this.entities[entity.id] = undefined;
24243                 });
24244             },
24245
24246
24247             revert: function(id) {
24248                 var baseEntity = this.base().entities[id];
24249                 var headEntity = this.entities[id];
24250                 if (headEntity === baseEntity) { return this; }
24251
24252                 return this.update(function() {
24253                     this._updateCalculated(headEntity, baseEntity);
24254                     delete this.entities[id];
24255                 });
24256             },
24257
24258
24259             update: function() {
24260                 var arguments$1 = arguments;
24261
24262                 var graph = this.frozen ? coreGraph(this, true) : this;
24263                 for (var i = 0; i < arguments.length; i++) {
24264                     arguments$1[i].call(graph, graph);
24265                 }
24266
24267                 if (this.frozen) { graph.frozen = true; }
24268
24269                 return graph;
24270             },
24271
24272
24273             // Obliterates any existing entities
24274             load: function(entities) {
24275                 var base = this.base();
24276                 this.entities = Object.create(base.entities);
24277
24278                 for (var i in entities) {
24279                     this.entities[i] = entities[i];
24280                     this._updateCalculated(base.entities[i], this.entities[i]);
24281                 }
24282
24283                 return this;
24284             }
24285         };
24286
24287         function osmTurn(turn) {
24288             if (!(this instanceof osmTurn)) {
24289                 return new osmTurn(turn);
24290             }
24291             Object.assign(this, turn);
24292         }
24293
24294
24295         function osmIntersection(graph, startVertexId, maxDistance) {
24296             maxDistance = maxDistance || 30;    // in meters
24297             var vgraph = coreGraph();           // virtual graph
24298             var i, j, k;
24299
24300
24301             function memberOfRestriction(entity) {
24302                 return graph.parentRelations(entity)
24303                     .some(function(r) { return r.isRestriction(); });
24304             }
24305
24306             function isRoad(way) {
24307                 if (way.isArea() || way.isDegenerate()) { return false; }
24308                 var roads = {
24309                     'motorway': true,
24310                     'motorway_link': true,
24311                     'trunk': true,
24312                     'trunk_link': true,
24313                     'primary': true,
24314                     'primary_link': true,
24315                     'secondary': true,
24316                     'secondary_link': true,
24317                     'tertiary': true,
24318                     'tertiary_link': true,
24319                     'residential': true,
24320                     'unclassified': true,
24321                     'living_street': true,
24322                     'service': true,
24323                     'road': true,
24324                     'track': true
24325                 };
24326                 return roads[way.tags.highway];
24327             }
24328
24329
24330             var startNode = graph.entity(startVertexId);
24331             var checkVertices = [startNode];
24332             var checkWays;
24333             var vertices = [];
24334             var vertexIds = [];
24335             var vertex;
24336             var ways = [];
24337             var wayIds = [];
24338             var way;
24339             var nodes = [];
24340             var node;
24341             var parents = [];
24342             var parent;
24343
24344             // `actions` will store whatever actions must be performed to satisfy
24345             // preconditions for adding a turn restriction to this intersection.
24346             //  - Remove any existing degenerate turn restrictions (missing from/to, etc)
24347             //  - Reverse oneways so that they are drawn in the forward direction
24348             //  - Split ways on key vertices
24349             var actions = [];
24350
24351
24352             // STEP 1:  walk the graph outwards from starting vertex to search
24353             //  for more key vertices and ways to include in the intersection..
24354
24355             while (checkVertices.length) {
24356                 vertex = checkVertices.pop();
24357
24358                 // check this vertex for parent ways that are roads
24359                 checkWays = graph.parentWays(vertex);
24360                 var hasWays = false;
24361                 for (i = 0; i < checkWays.length; i++) {
24362                     way = checkWays[i];
24363                     if (!isRoad(way) && !memberOfRestriction(way)) { continue; }
24364
24365                     ways.push(way);   // it's a road, or it's already in a turn restriction
24366                     hasWays = true;
24367
24368                     // check the way's children for more key vertices
24369                     nodes = utilArrayUniq(graph.childNodes(way));
24370                     for (j = 0; j < nodes.length; j++) {
24371                         node = nodes[j];
24372                         if (node === vertex) { continue; }                                           // same thing
24373                         if (vertices.indexOf(node) !== -1) { continue; }                             // seen it already
24374                         if (geoSphericalDistance(node.loc, startNode.loc) > maxDistance) { continue; }   // too far from start
24375
24376                         // a key vertex will have parents that are also roads
24377                         var hasParents = false;
24378                         parents = graph.parentWays(node);
24379                         for (k = 0; k < parents.length; k++) {
24380                             parent = parents[k];
24381                             if (parent === way) { continue; }                 // same thing
24382                             if (ways.indexOf(parent) !== -1) { continue; }    // seen it already
24383                             if (!isRoad(parent)) { continue; }                // not a road
24384                             hasParents = true;
24385                             break;
24386                         }
24387
24388                         if (hasParents) {
24389                             checkVertices.push(node);
24390                         }
24391                     }
24392                 }
24393
24394                 if (hasWays) {
24395                     vertices.push(vertex);
24396                 }
24397             }
24398
24399             vertices = utilArrayUniq(vertices);
24400             ways = utilArrayUniq(ways);
24401
24402
24403             // STEP 2:  Build a virtual graph containing only the entities in the intersection..
24404             // Everything done after this step should act on the virtual graph
24405             // Any actions that must be performed later to the main graph go in `actions` array
24406             ways.forEach(function(way) {
24407                 graph.childNodes(way).forEach(function(node) {
24408                     vgraph = vgraph.replace(node);
24409                 });
24410
24411                 vgraph = vgraph.replace(way);
24412
24413                 graph.parentRelations(way).forEach(function(relation) {
24414                     if (relation.isRestriction()) {
24415                         if (relation.isValidRestriction(graph)) {
24416                             vgraph = vgraph.replace(relation);
24417                         } else if (relation.isComplete(graph)) {
24418                             actions.push(actionDeleteRelation(relation.id));
24419                         }
24420                     }
24421                 });
24422             });
24423
24424
24425             // STEP 3:  Force all oneways to be drawn in the forward direction
24426             ways.forEach(function(w) {
24427                 var way = vgraph.entity(w.id);
24428                 if (way.tags.oneway === '-1') {
24429                     var action = actionReverse(way.id, { reverseOneway: true });
24430                     actions.push(action);
24431                     vgraph = action(vgraph);
24432                 }
24433             });
24434
24435
24436             // STEP 4:  Split ways on key vertices
24437             var origCount = osmEntity.id.next.way;
24438             vertices.forEach(function(v) {
24439                 // This is an odd way to do it, but we need to find all the ways that
24440                 // will be split here, then split them one at a time to ensure that these
24441                 // actions can be replayed on the main graph exactly in the same order.
24442                 // (It is unintuitive, but the order of ways returned from graph.parentWays()
24443                 // is arbitrary, depending on how the main graph and vgraph were built)
24444                 var splitAll = actionSplit(v.id);
24445                 if (!splitAll.disabled(vgraph)) {
24446                     splitAll.ways(vgraph).forEach(function(way) {
24447                         var splitOne = actionSplit(v.id).limitWays([way.id]);
24448                         actions.push(splitOne);
24449                         vgraph = splitOne(vgraph);
24450                     });
24451                 }
24452             });
24453
24454             // In here is where we should also split the intersection at nearby junction.
24455             //   for https://github.com/mapbox/iD-internal/issues/31
24456             // nearbyVertices.forEach(function(v) {
24457             // });
24458
24459             // Reasons why we reset the way id count here:
24460             //  1. Continuity with way ids created by the splits so that we can replay
24461             //     these actions later if the user decides to create a turn restriction
24462             //  2. Avoids churning way ids just by hovering over a vertex
24463             //     and displaying the turn restriction editor
24464             osmEntity.id.next.way = origCount;
24465
24466
24467             // STEP 5:  Update arrays to point to vgraph entities
24468             vertexIds = vertices.map(function(v) { return v.id; });
24469             vertices = [];
24470             ways = [];
24471
24472             vertexIds.forEach(function(id) {
24473                 var vertex = vgraph.entity(id);
24474                 var parents = vgraph.parentWays(vertex);
24475                 vertices.push(vertex);
24476                 ways = ways.concat(parents);
24477             });
24478
24479             vertices = utilArrayUniq(vertices);
24480             ways = utilArrayUniq(ways);
24481
24482             vertexIds = vertices.map(function(v) { return v.id; });
24483             wayIds = ways.map(function(w) { return w.id; });
24484
24485
24486             // STEP 6:  Update the ways with some metadata that will be useful for
24487             // walking the intersection graph later and rendering turn arrows.
24488
24489             function withMetadata(way, vertexIds) {
24490                 var __oneWay = way.isOneWay();
24491
24492                 // which affixes are key vertices?
24493                 var __first = (vertexIds.indexOf(way.first()) !== -1);
24494                 var __last = (vertexIds.indexOf(way.last()) !== -1);
24495
24496                 // what roles is this way eligible for?
24497                 var __via = (__first && __last);
24498                 var __from = ((__first && !__oneWay) || __last);
24499                 var __to = (__first || (__last && !__oneWay));
24500
24501                 return way.update({
24502                     __first:  __first,
24503                     __last:  __last,
24504                     __from:  __from,
24505                     __via: __via,
24506                     __to:  __to,
24507                     __oneWay:  __oneWay
24508                 });
24509             }
24510
24511             ways = [];
24512             wayIds.forEach(function(id) {
24513                 var way = withMetadata(vgraph.entity(id), vertexIds);
24514                 vgraph = vgraph.replace(way);
24515                 ways.push(way);
24516             });
24517
24518
24519             // STEP 7:  Simplify - This is an iterative process where we:
24520             //  1. Find trivial vertices with only 2 parents
24521             //  2. trim off the leaf way from those vertices and remove from vgraph
24522
24523             var keepGoing;
24524             var removeWayIds = [];
24525             var removeVertexIds = [];
24526
24527             do {
24528                 keepGoing = false;
24529                 checkVertices = vertexIds.slice();
24530
24531                 for (i = 0; i < checkVertices.length; i++) {
24532                     var vertexId = checkVertices[i];
24533                     vertex = vgraph.hasEntity(vertexId);
24534
24535                     if (!vertex) {
24536                         if (vertexIds.indexOf(vertexId) !== -1) {
24537                             vertexIds.splice(vertexIds.indexOf(vertexId), 1);   // stop checking this one
24538                         }
24539                         removeVertexIds.push(vertexId);
24540                         continue;
24541                     }
24542
24543                     parents = vgraph.parentWays(vertex);
24544                     if (parents.length < 3) {
24545                         if (vertexIds.indexOf(vertexId) !== -1) {
24546                             vertexIds.splice(vertexIds.indexOf(vertexId), 1);   // stop checking this one
24547                         }
24548                     }
24549
24550                     if (parents.length === 2) {     // vertex with 2 parents is trivial
24551                         var a = parents[0];
24552                         var b = parents[1];
24553                         var aIsLeaf = a && !a.__via;
24554                         var bIsLeaf = b && !b.__via;
24555                         var leaf, survivor;
24556
24557                         if (aIsLeaf && !bIsLeaf) {
24558                             leaf = a;
24559                             survivor = b;
24560                         } else if (!aIsLeaf && bIsLeaf) {
24561                             leaf = b;
24562                             survivor = a;
24563                         }
24564
24565                         if (leaf && survivor) {
24566                             survivor = withMetadata(survivor, vertexIds);      // update survivor way
24567                             vgraph = vgraph.replace(survivor).remove(leaf);    // update graph
24568                             removeWayIds.push(leaf.id);
24569                             keepGoing = true;
24570                         }
24571                     }
24572
24573                     parents = vgraph.parentWays(vertex);
24574
24575                     if (parents.length < 2) {     // vertex is no longer a key vertex
24576                         if (vertexIds.indexOf(vertexId) !== -1) {
24577                             vertexIds.splice(vertexIds.indexOf(vertexId), 1);   // stop checking this one
24578                         }
24579                         removeVertexIds.push(vertexId);
24580                         keepGoing = true;
24581                     }
24582
24583                     if (parents.length < 1) {     // vertex is no longer attached to anything
24584                         vgraph = vgraph.remove(vertex);
24585                     }
24586
24587                 }
24588             } while (keepGoing);
24589
24590
24591             vertices = vertices
24592                 .filter(function(vertex) { return removeVertexIds.indexOf(vertex.id) === -1; })
24593                 .map(function(vertex) { return vgraph.entity(vertex.id); });
24594             ways = ways
24595                 .filter(function(way) { return removeWayIds.indexOf(way.id) === -1; })
24596                 .map(function(way) { return vgraph.entity(way.id); });
24597
24598
24599             // OK!  Here is our intersection..
24600             var intersection = {
24601                 graph: vgraph,
24602                 actions: actions,
24603                 vertices: vertices,
24604                 ways: ways,
24605             };
24606
24607
24608
24609             // Get all the valid turns through this intersection given a starting way id.
24610             // This operates on the virtual graph for everything.
24611             //
24612             // Basically, walk through all possible paths from starting way,
24613             //   honoring the existing turn restrictions as we go (watch out for loops!)
24614             //
24615             // For each path found, generate and return a `osmTurn` datastructure.
24616             //
24617             intersection.turns = function(fromWayId, maxViaWay) {
24618                 if (!fromWayId) { return []; }
24619                 if (!maxViaWay) { maxViaWay = 0; }
24620
24621                 var vgraph = intersection.graph;
24622                 var keyVertexIds = intersection.vertices.map(function(v) { return v.id; });
24623
24624                 var start = vgraph.entity(fromWayId);
24625                 if (!start || !(start.__from || start.__via)) { return []; }
24626
24627                 // maxViaWay=0   from-*-to              (0 vias)
24628                 // maxViaWay=1   from-*-via-*-to        (1 via max)
24629                 // maxViaWay=2   from-*-via-*-via-*-to  (2 vias max)
24630                 var maxPathLength = (maxViaWay * 2) + 3;
24631                 var turns = [];
24632
24633                 step(start);
24634                 return turns;
24635
24636
24637                 // traverse the intersection graph and find all the valid paths
24638                 function step(entity, currPath, currRestrictions, matchedRestriction) {
24639                     currPath = (currPath || []).slice();  // shallow copy
24640                     if (currPath.length >= maxPathLength) { return; }
24641                     currPath.push(entity.id);
24642                     currRestrictions = (currRestrictions || []).slice();  // shallow copy
24643                     var i, j;
24644
24645                     if (entity.type === 'node') {
24646                         var parents = vgraph.parentWays(entity);
24647                         var nextWays = [];
24648
24649                         // which ways can we step into?
24650                         for (i = 0; i < parents.length; i++) {
24651                             var way = parents[i];
24652
24653                             // if next way is a oneway incoming to this vertex, skip
24654                             if (way.__oneWay && way.nodes[0] !== entity.id) { continue; }
24655
24656                             // if we have seen it before (allowing for an initial u-turn), skip
24657                             if (currPath.indexOf(way.id) !== -1 && currPath.length >= 3) { continue; }
24658
24659                             // Check all "current" restrictions (where we've already walked the `FROM`)
24660                             var restrict = undefined;
24661                             for (j = 0; j < currRestrictions.length; j++) {
24662                                 var restriction = currRestrictions[j];
24663                                 var f = restriction.memberByRole('from');
24664                                 var v = restriction.membersByRole('via');
24665                                 var t = restriction.memberByRole('to');
24666                                 var isOnly = /^only_/.test(restriction.tags.restriction);
24667
24668                                 // Does the current path match this turn restriction?
24669                                 var matchesFrom = (f.id === fromWayId);
24670                                 var matchesViaTo = false;
24671                                 var isAlongOnlyPath = false;
24672
24673                                 if (t.id === way.id) {     // match TO
24674
24675                                     if (v.length === 1 && v[0].type === 'node') {    // match VIA node
24676                                         matchesViaTo = (v[0].id === entity.id && (
24677                                             (matchesFrom && currPath.length === 2) ||
24678                                             (!matchesFrom && currPath.length > 2)
24679                                         ));
24680
24681                                     } else {                                         // match all VIA ways
24682                                         var pathVias = [];
24683                                         for (k = 2; k < currPath.length; k +=2 ) {   // k = 2 skips FROM
24684                                             pathVias.push(currPath[k]);              // (path goes way-node-way...)
24685                                         }
24686                                         var restrictionVias = [];
24687                                         for (k = 0; k < v.length; k++) {
24688                                             if (v[k].type === 'way') {
24689                                                 restrictionVias.push(v[k].id);
24690                                             }
24691                                         }
24692                                         var diff = utilArrayDifference(pathVias, restrictionVias);
24693                                         matchesViaTo = !diff.length;
24694                                     }
24695
24696                                 } else if (isOnly) {
24697                                     for (k = 0; k < v.length; k++) {
24698                                         // way doesn't match TO, but is one of the via ways along the path of an "only"
24699                                         if (v[k].type === 'way' && v[k].id === way.id) {
24700                                             isAlongOnlyPath = true;
24701                                             break;
24702                                         }
24703                                     }
24704                                 }
24705
24706                                 if (matchesViaTo) {
24707                                     if (isOnly) {
24708                                         restrict = { id: restriction.id, direct: matchesFrom, from: f.id, only: true, end: true };
24709                                     } else {
24710                                         restrict = { id: restriction.id, direct: matchesFrom, from: f.id, no: true, end: true };
24711                                     }
24712                                 } else {    // indirect - caused by a different nearby restriction
24713                                     if (isAlongOnlyPath) {
24714                                         restrict = { id: restriction.id, direct: false, from: f.id, only: true, end: false };
24715                                     } else if (isOnly) {
24716                                         restrict = { id: restriction.id, direct: false, from: f.id, no: true, end: true };
24717                                     }
24718                                 }
24719
24720                                 // stop looking if we find a "direct" restriction (matching FROM, VIA, TO)
24721                                 if (restrict && restrict.direct)
24722                                     { break; }
24723                             }
24724
24725                             nextWays.push({ way: way, restrict: restrict });
24726                         }
24727
24728                         nextWays.forEach(function(nextWay) {
24729                             step(nextWay.way, currPath, currRestrictions, nextWay.restrict);
24730                         });
24731
24732
24733                     } else {  // entity.type === 'way'
24734                         if (currPath.length >= 3) {     // this is a "complete" path..
24735                             var turnPath = currPath.slice();   // shallow copy
24736
24737                             // an indirect restriction - only include the partial path (starting at FROM)
24738                             if (matchedRestriction && matchedRestriction.direct === false) {
24739                                 for (i = 0; i < turnPath.length; i++) {
24740                                     if (turnPath[i] === matchedRestriction.from) {
24741                                         turnPath = turnPath.slice(i);
24742                                         break;
24743                                     }
24744                                 }
24745                             }
24746
24747                             var turn = pathToTurn(turnPath);
24748                             if (turn) {
24749                                 if (matchedRestriction) {
24750                                     turn.restrictionID = matchedRestriction.id;
24751                                     turn.no = matchedRestriction.no;
24752                                     turn.only = matchedRestriction.only;
24753                                     turn.direct = matchedRestriction.direct;
24754                                 }
24755                                 turns.push(osmTurn(turn));
24756                             }
24757
24758                             if (currPath[0] === currPath[2]) { return; }   // if we made a u-turn - stop here
24759                         }
24760
24761                         if (matchedRestriction && matchedRestriction.end) { return; }  // don't advance any further
24762
24763                         // which nodes can we step into?
24764                         var n1 = vgraph.entity(entity.first());
24765                         var n2 = vgraph.entity(entity.last());
24766                         var dist = geoSphericalDistance(n1.loc, n2.loc);
24767                         var nextNodes = [];
24768
24769                         if (currPath.length > 1) {
24770                             if (dist > maxDistance) { return; }   // the next node is too far
24771                             if (!entity.__via) { return; }        // this way is a leaf / can't be a via
24772                         }
24773
24774                         if (!entity.__oneWay &&                     // bidirectional..
24775                             keyVertexIds.indexOf(n1.id) !== -1 &&   // key vertex..
24776                             currPath.indexOf(n1.id) === -1) {       // haven't seen it yet..
24777                             nextNodes.push(n1);                     // can advance to first node
24778                         }
24779                         if (keyVertexIds.indexOf(n2.id) !== -1 &&   // key vertex..
24780                             currPath.indexOf(n2.id) === -1) {       // haven't seen it yet..
24781                             nextNodes.push(n2);                     // can advance to last node
24782                         }
24783
24784                         nextNodes.forEach(function(nextNode) {
24785                             // gather restrictions FROM this way
24786                             var fromRestrictions = vgraph.parentRelations(entity).filter(function(r) {
24787                                 if (!r.isRestriction()) { return false; }
24788
24789                                 var f = r.memberByRole('from');
24790                                 if (!f || f.id !== entity.id) { return false; }
24791
24792                                 var isOnly = /^only_/.test(r.tags.restriction);
24793                                 if (!isOnly) { return true; }
24794
24795                                 // `only_` restrictions only matter along the direction of the VIA - #4849
24796                                 var isOnlyVia = false;
24797                                 var v = r.membersByRole('via');
24798                                 if (v.length === 1 && v[0].type === 'node') {   // via node
24799                                     isOnlyVia = (v[0].id === nextNode.id);
24800                                 } else {                                        // via way(s)
24801                                     for (var i = 0; i < v.length; i++) {
24802                                         if (v[i].type !== 'way') { continue; }
24803                                         var viaWay = vgraph.entity(v[i].id);
24804                                         if (viaWay.first() === nextNode.id || viaWay.last() === nextNode.id) {
24805                                             isOnlyVia = true;
24806                                             break;
24807                                         }
24808                                     }
24809                                 }
24810                                 return isOnlyVia;
24811                             });
24812
24813                             step(nextNode, currPath, currRestrictions.concat(fromRestrictions), false);
24814                         });
24815                     }
24816                 }
24817
24818
24819                 // assumes path is alternating way-node-way of odd length
24820                 function pathToTurn(path) {
24821                     if (path.length < 3) { return; }
24822                     var fromWayId, fromNodeId, fromVertexId;
24823                     var toWayId, toNodeId, toVertexId;
24824                     var viaWayIds, viaNodeId, isUturn;
24825
24826                     fromWayId = path[0];
24827                     toWayId = path[path.length - 1];
24828
24829                     if (path.length === 3 && fromWayId === toWayId) {  // u turn
24830                         var way = vgraph.entity(fromWayId);
24831                         if (way.__oneWay) { return null; }
24832
24833                         isUturn = true;
24834                         viaNodeId = fromVertexId = toVertexId = path[1];
24835                         fromNodeId = toNodeId = adjacentNode(fromWayId, viaNodeId);
24836
24837                     } else {
24838                         isUturn = false;
24839                         fromVertexId = path[1];
24840                         fromNodeId = adjacentNode(fromWayId, fromVertexId);
24841                         toVertexId = path[path.length - 2];
24842                         toNodeId = adjacentNode(toWayId, toVertexId);
24843
24844                         if (path.length === 3) {
24845                             viaNodeId = path[1];
24846                         } else {
24847                             viaWayIds = path.filter(function(entityId) { return entityId[0] === 'w'; });
24848                             viaWayIds = viaWayIds.slice(1, viaWayIds.length - 1);  // remove first, last
24849                         }
24850                     }
24851
24852                     return {
24853                         key:  path.join('_'),
24854                         path: path,
24855                         from: { node: fromNodeId, way:  fromWayId, vertex: fromVertexId },
24856                         via:  { node: viaNodeId,  ways: viaWayIds },
24857                         to:   { node: toNodeId,   way:  toWayId, vertex: toVertexId },
24858                         u:    isUturn
24859                     };
24860
24861
24862                     function adjacentNode(wayId, affixId) {
24863                         var nodes = vgraph.entity(wayId).nodes;
24864                         return affixId === nodes[0] ? nodes[1] : nodes[nodes.length - 2];
24865                     }
24866                 }
24867
24868             };
24869
24870             return intersection;
24871         }
24872
24873
24874         function osmInferRestriction(graph, turn, projection) {
24875             var fromWay = graph.entity(turn.from.way);
24876             var fromNode = graph.entity(turn.from.node);
24877             var fromVertex = graph.entity(turn.from.vertex);
24878             var toWay = graph.entity(turn.to.way);
24879             var toNode = graph.entity(turn.to.node);
24880             var toVertex = graph.entity(turn.to.vertex);
24881
24882             var fromOneWay = (fromWay.tags.oneway === 'yes');
24883             var toOneWay = (toWay.tags.oneway === 'yes');
24884             var angle = (geoAngle(fromVertex, fromNode, projection) -
24885                         geoAngle(toVertex, toNode, projection)) * 180 / Math.PI;
24886
24887             while (angle < 0)
24888                 { angle += 360; }
24889
24890             if (fromNode === toNode)
24891                 { return 'no_u_turn'; }
24892             if ((angle < 23 || angle > 336) && fromOneWay && toOneWay)
24893                 { return 'no_u_turn'; }   // wider tolerance for u-turn if both ways are oneway
24894             if ((angle < 40 || angle > 319) && fromOneWay && toOneWay && turn.from.vertex !== turn.to.vertex)
24895                 { return 'no_u_turn'; }   // even wider tolerance for u-turn if there is a via way (from !== to)
24896             if (angle < 158)
24897                 { return 'no_right_turn'; }
24898             if (angle > 202)
24899                 { return 'no_left_turn'; }
24900
24901             return 'no_straight_on';
24902         }
24903
24904         function actionMergePolygon(ids, newRelationId) {
24905
24906             function groupEntities(graph) {
24907                 var entities = ids.map(function (id) { return graph.entity(id); });
24908                 var geometryGroups = utilArrayGroupBy(entities, function(entity) {
24909                     if (entity.type === 'way' && entity.isClosed()) {
24910                         return 'closedWay';
24911                     } else if (entity.type === 'relation' && entity.isMultipolygon()) {
24912                         return 'multipolygon';
24913                     } else {
24914                         return 'other';
24915                     }
24916                 });
24917
24918                 return Object.assign(
24919                     { closedWay: [], multipolygon: [], other: [] },
24920                     geometryGroups
24921                 );
24922             }
24923
24924
24925             var action = function(graph) {
24926                 var entities = groupEntities(graph);
24927
24928                 // An array representing all the polygons that are part of the multipolygon.
24929                 //
24930                 // Each element is itself an array of objects with an id property, and has a
24931                 // locs property which is an array of the locations forming the polygon.
24932                 var polygons = entities.multipolygon.reduce(function(polygons, m) {
24933                     return polygons.concat(osmJoinWays(m.members, graph));
24934                 }, []).concat(entities.closedWay.map(function(d) {
24935                     var member = [{id: d.id}];
24936                     member.nodes = graph.childNodes(d);
24937                     return member;
24938                 }));
24939
24940                 // contained is an array of arrays of boolean values,
24941                 // where contained[j][k] is true iff the jth way is
24942                 // contained by the kth way.
24943                 var contained = polygons.map(function(w, i) {
24944                     return polygons.map(function(d, n) {
24945                         if (i === n) { return null; }
24946                         return geoPolygonContainsPolygon(
24947                             d.nodes.map(function(n) { return n.loc; }),
24948                             w.nodes.map(function(n) { return n.loc; })
24949                         );
24950                     });
24951                 });
24952
24953                 // Sort all polygons as either outer or inner ways
24954                 var members = [];
24955                 var outer = true;
24956
24957                 while (polygons.length) {
24958                     extractUncontained(polygons);
24959                     polygons = polygons.filter(isContained);
24960                     contained = contained.filter(isContained).map(filterContained);
24961                 }
24962
24963                 function isContained(d, i) {
24964                     return contained[i].some(function(val) { return val; });
24965                 }
24966
24967                 function filterContained(d) {
24968                     return d.filter(isContained);
24969                 }
24970
24971                 function extractUncontained(polygons) {
24972                     polygons.forEach(function(d, i) {
24973                         if (!isContained(d, i)) {
24974                             d.forEach(function(member) {
24975                                 members.push({
24976                                     type: 'way',
24977                                     id: member.id,
24978                                     role: outer ? 'outer' : 'inner'
24979                                 });
24980                             });
24981                         }
24982                     });
24983                     outer = !outer;
24984                 }
24985
24986                 // Move all tags to one relation
24987                 var relation = entities.multipolygon[0] ||
24988                     osmRelation({ id: newRelationId, tags: { type: 'multipolygon' }});
24989
24990                 entities.multipolygon.slice(1).forEach(function(m) {
24991                     relation = relation.mergeTags(m.tags);
24992                     graph = graph.remove(m);
24993                 });
24994
24995                 entities.closedWay.forEach(function(way) {
24996                     function isThisOuter(m) {
24997                         return m.id === way.id && m.role !== 'inner';
24998                     }
24999                     if (members.some(isThisOuter)) {
25000                         relation = relation.mergeTags(way.tags);
25001                         graph = graph.replace(way.update({ tags: {} }));
25002                     }
25003                 });
25004
25005                 return graph.replace(relation.update({
25006                     members: members,
25007                     tags: utilObjectOmit(relation.tags, ['area'])
25008                 }));
25009             };
25010
25011
25012             action.disabled = function(graph) {
25013                 var entities = groupEntities(graph);
25014                 if (entities.other.length > 0 ||
25015                     entities.closedWay.length + entities.multipolygon.length < 2) {
25016                     return 'not_eligible';
25017                 }
25018                 if (!entities.multipolygon.every(function(r) { return r.isComplete(graph); })) {
25019                     return 'incomplete_relation';
25020                 }
25021
25022                 if (!entities.multipolygon.length) {
25023                     var sharedMultipolygons = [];
25024                     entities.closedWay.forEach(function(way, i) {
25025                         if (i === 0) {
25026                             sharedMultipolygons = graph.parentMultipolygons(way);
25027                         } else {
25028                             sharedMultipolygons = utilArrayIntersection(sharedMultipolygons, graph.parentMultipolygons(way));
25029                         }
25030                     });
25031                     sharedMultipolygons = sharedMultipolygons.filter(function(relation) {
25032                         return relation.members.length === entities.closedWay.length;
25033                     });
25034                     if (sharedMultipolygons.length) {
25035                         // don't create a new multipolygon if it'd be redundant
25036                         return 'not_eligible';
25037                     }
25038                 } else if (entities.closedWay.some(function(way) {
25039                         return utilArrayIntersection(graph.parentMultipolygons(way), entities.multipolygon).length;
25040                     })) {
25041                     // don't add a way to a multipolygon again if it's already a member
25042                     return 'not_eligible';
25043                 }
25044             };
25045
25046
25047             return action;
25048         }
25049
25050         // do not edit .js files directly - edit src/index.jst
25051
25052
25053
25054         var fastDeepEqual = function equal(a, b) {
25055           if (a === b) { return true; }
25056
25057           if (a && b && typeof a == 'object' && typeof b == 'object') {
25058             if (a.constructor !== b.constructor) { return false; }
25059
25060             var length, i, keys;
25061             if (Array.isArray(a)) {
25062               length = a.length;
25063               if (length != b.length) { return false; }
25064               for (i = length; i-- !== 0;)
25065                 { if (!equal(a[i], b[i])) { return false; } }
25066               return true;
25067             }
25068
25069
25070
25071             if (a.constructor === RegExp) { return a.source === b.source && a.flags === b.flags; }
25072             if (a.valueOf !== Object.prototype.valueOf) { return a.valueOf() === b.valueOf(); }
25073             if (a.toString !== Object.prototype.toString) { return a.toString() === b.toString(); }
25074
25075             keys = Object.keys(a);
25076             length = keys.length;
25077             if (length !== Object.keys(b).length) { return false; }
25078
25079             for (i = length; i-- !== 0;)
25080               { if (!Object.prototype.hasOwnProperty.call(b, keys[i])) { return false; } }
25081
25082             for (i = length; i-- !== 0;) {
25083               var key = keys[i];
25084
25085               if (!equal(a[key], b[key])) { return false; }
25086             }
25087
25088             return true;
25089           }
25090
25091           // true if both NaN, false otherwise
25092           return a!==a && b!==b;
25093         };
25094
25095         // Text diff algorithm following Hunt and McIlroy 1976.
25096         // J. W. Hunt and M. D. McIlroy, An algorithm for differential buffer
25097         // comparison, Bell Telephone Laboratories CSTR #41 (1976)
25098         // http://www.cs.dartmouth.edu/~doug/
25099         // https://en.wikipedia.org/wiki/Longest_common_subsequence_problem
25100         //
25101         // Expects two arrays, finds longest common sequence
25102         function LCS(buffer1, buffer2) {
25103
25104           var equivalenceClasses = {};
25105           for (var j = 0; j < buffer2.length; j++) {
25106             var item = buffer2[j];
25107             if (equivalenceClasses[item]) {
25108               equivalenceClasses[item].push(j);
25109             } else {
25110               equivalenceClasses[item] = [j];
25111             }
25112           }
25113
25114           var NULLRESULT = { buffer1index: -1, buffer2index: -1, chain: null };
25115           var candidates = [NULLRESULT];
25116
25117           for (var i = 0; i < buffer1.length; i++) {
25118             var item$1 = buffer1[i];
25119             var buffer2indices = equivalenceClasses[item$1] || [];
25120             var r = 0;
25121             var c = candidates[0];
25122
25123             for (var jx = 0; jx < buffer2indices.length; jx++) {
25124               var j$1 = buffer2indices[jx];
25125
25126               var s = (void 0);
25127               for (s = r; s < candidates.length; s++) {
25128                 if ((candidates[s].buffer2index < j$1) && ((s === candidates.length - 1) || (candidates[s + 1].buffer2index > j$1))) {
25129                   break;
25130                 }
25131               }
25132
25133               if (s < candidates.length) {
25134                 var newCandidate = { buffer1index: i, buffer2index: j$1, chain: candidates[s] };
25135                 if (r === candidates.length) {
25136                   candidates.push(c);
25137                 } else {
25138                   candidates[r] = c;
25139                 }
25140                 r = s + 1;
25141                 c = newCandidate;
25142                 if (r === candidates.length) {
25143                   break; // no point in examining further (j)s
25144                 }
25145               }
25146             }
25147
25148             candidates[r] = c;
25149           }
25150
25151           // At this point, we know the LCS: it's in the reverse of the
25152           // linked-list through .chain of candidates[candidates.length - 1].
25153
25154           return candidates[candidates.length - 1];
25155         }
25156
25157
25158         // We apply the LCS to give a simple representation of the
25159         // offsets and lengths of mismatched chunks in the input
25160         // buffers. This is used by diff3MergeRegions.
25161         function diffIndices(buffer1, buffer2) {
25162           var lcs = LCS(buffer1, buffer2);
25163           var result = [];
25164           var tail1 = buffer1.length;
25165           var tail2 = buffer2.length;
25166
25167           for (var candidate = lcs; candidate !== null; candidate = candidate.chain) {
25168             var mismatchLength1 = tail1 - candidate.buffer1index - 1;
25169             var mismatchLength2 = tail2 - candidate.buffer2index - 1;
25170             tail1 = candidate.buffer1index;
25171             tail2 = candidate.buffer2index;
25172
25173             if (mismatchLength1 || mismatchLength2) {
25174               result.push({
25175                 buffer1: [tail1 + 1, mismatchLength1],
25176                 buffer1Content: buffer1.slice(tail1 + 1, tail1 + 1 + mismatchLength1),
25177                 buffer2: [tail2 + 1, mismatchLength2],
25178                 buffer2Content: buffer2.slice(tail2 + 1, tail2 + 1 + mismatchLength2)
25179               });
25180             }
25181           }
25182
25183           result.reverse();
25184           return result;
25185         }
25186
25187
25188         // Given three buffers, A, O, and B, where both A and B are
25189         // independently derived from O, returns a fairly complicated
25190         // internal representation of merge decisions it's taken. The
25191         // interested reader may wish to consult
25192         //
25193         // Sanjeev Khanna, Keshav Kunal, and Benjamin C. Pierce.
25194         // 'A Formal Investigation of ' In Arvind and Prasad,
25195         // editors, Foundations of Software Technology and Theoretical
25196         // Computer Science (FSTTCS), December 2007.
25197         //
25198         // (http://www.cis.upenn.edu/~bcpierce/papers/diff3-short.pdf)
25199         //
25200         function diff3MergeRegions(a, o, b) {
25201
25202           // "hunks" are array subsets where `a` or `b` are different from `o`
25203           // https://www.gnu.org/software/diffutils/manual/html_node/diff3-Hunks.html
25204           var hunks = [];
25205           function addHunk(h, ab) {
25206             hunks.push({
25207               ab: ab,
25208               oStart: h.buffer1[0],
25209               oLength: h.buffer1[1],   // length of o to remove
25210               abStart: h.buffer2[0],
25211               abLength: h.buffer2[1]   // length of a/b to insert
25212               // abContent: (ab === 'a' ? a : b).slice(h.buffer2[0], h.buffer2[0] + h.buffer2[1])
25213             });
25214           }
25215
25216           diffIndices(o, a).forEach(function (item) { return addHunk(item, 'a'); });
25217           diffIndices(o, b).forEach(function (item) { return addHunk(item, 'b'); });
25218           hunks.sort(function (x,y) { return x.oStart - y.oStart; });
25219
25220           var results = [];
25221           var currOffset = 0;
25222
25223           function advanceTo(endOffset) {
25224             if (endOffset > currOffset) {
25225               results.push({
25226                 stable: true,
25227                 buffer: 'o',
25228                 bufferStart: currOffset,
25229                 bufferLength: endOffset - currOffset,
25230                 bufferContent: o.slice(currOffset, endOffset)
25231               });
25232               currOffset = endOffset;
25233             }
25234           }
25235
25236           while (hunks.length) {
25237             var hunk = hunks.shift();
25238             var regionStart = hunk.oStart;
25239             var regionEnd = hunk.oStart + hunk.oLength;
25240             var regionHunks = [hunk];
25241             advanceTo(regionStart);
25242
25243             // Try to pull next overlapping hunk into this region
25244             while (hunks.length) {
25245               var nextHunk = hunks[0];
25246               var nextHunkStart = nextHunk.oStart;
25247               if (nextHunkStart > regionEnd) { break; }   // no overlap
25248
25249               regionEnd = Math.max(regionEnd, nextHunkStart + nextHunk.oLength);
25250               regionHunks.push(hunks.shift());
25251             }
25252
25253             if (regionHunks.length === 1) {
25254               // Only one hunk touches this region, meaning that there is no conflict here.
25255               // Either `a` or `b` is inserting into a region of `o` unchanged by the other.
25256               if (hunk.abLength > 0) {
25257                 var buffer = (hunk.ab === 'a' ? a : b);
25258                 results.push({
25259                   stable: true,
25260                   buffer: hunk.ab,
25261                   bufferStart: hunk.abStart,
25262                   bufferLength: hunk.abLength,
25263                   bufferContent: buffer.slice(hunk.abStart, hunk.abStart + hunk.abLength)
25264                 });
25265               }
25266             } else {
25267               // A true a/b conflict. Determine the bounds involved from `a`, `o`, and `b`.
25268               // Effectively merge all the `a` hunks into one giant hunk, then do the
25269               // same for the `b` hunks; then, correct for skew in the regions of `o`
25270               // that each side changed, and report appropriate spans for the three sides.
25271               var bounds = {
25272                 a: [a.length, -1, o.length, -1],
25273                 b: [b.length, -1, o.length, -1]
25274               };
25275               while (regionHunks.length) {
25276                 hunk = regionHunks.shift();
25277                 var oStart = hunk.oStart;
25278                 var oEnd = oStart + hunk.oLength;
25279                 var abStart = hunk.abStart;
25280                 var abEnd = abStart + hunk.abLength;
25281                 var b$1 = bounds[hunk.ab];
25282                 b$1[0] = Math.min(abStart, b$1[0]);
25283                 b$1[1] = Math.max(abEnd, b$1[1]);
25284                 b$1[2] = Math.min(oStart, b$1[2]);
25285                 b$1[3] = Math.max(oEnd, b$1[3]);
25286               }
25287
25288               var aStart = bounds.a[0] + (regionStart - bounds.a[2]);
25289               var aEnd = bounds.a[1] + (regionEnd - bounds.a[3]);
25290               var bStart = bounds.b[0] + (regionStart - bounds.b[2]);
25291               var bEnd = bounds.b[1] + (regionEnd - bounds.b[3]);
25292
25293               var result = {
25294                 stable: false,
25295                 aStart: aStart,
25296                 aLength: aEnd - aStart,
25297                 aContent: a.slice(aStart, aEnd),
25298                 oStart: regionStart,
25299                 oLength: regionEnd - regionStart,
25300                 oContent: o.slice(regionStart, regionEnd),
25301                 bStart: bStart,
25302                 bLength: bEnd - bStart,
25303                 bContent: b.slice(bStart, bEnd)
25304               };
25305               results.push(result);
25306             }
25307             currOffset = regionEnd;
25308           }
25309
25310           advanceTo(o.length);
25311
25312           return results;
25313         }
25314
25315
25316         // Applies the output of diff3MergeRegions to actually
25317         // construct the merged buffer; the returned result alternates
25318         // between 'ok' and 'conflict' blocks.
25319         // A "false conflict" is where `a` and `b` both change the same from `o`
25320         function diff3Merge(a, o, b, options) {
25321           var defaults = {
25322             excludeFalseConflicts: true,
25323             stringSeparator: /\s+/
25324           };
25325           options = Object.assign(defaults, options);
25326
25327           var aString = (typeof a === 'string');
25328           var oString = (typeof o === 'string');
25329           var bString = (typeof b === 'string');
25330
25331           if (aString) { a = a.split(options.stringSeparator); }
25332           if (oString) { o = o.split(options.stringSeparator); }
25333           if (bString) { b = b.split(options.stringSeparator); }
25334
25335           var results = [];
25336           var regions = diff3MergeRegions(a, o, b);
25337
25338           var okBuffer = [];
25339           function flushOk() {
25340             if (okBuffer.length) {
25341               results.push({ ok: okBuffer });
25342             }
25343             okBuffer = [];
25344           }
25345
25346           function isFalseConflict(a, b) {
25347             if (a.length !== b.length) { return false; }
25348             for (var i = 0; i < a.length; i++) {
25349               if (a[i] !== b[i]) { return false; }
25350             }
25351             return true;
25352           }
25353
25354           regions.forEach(function (region) {
25355             if (region.stable) {
25356               okBuffer.push.apply(okBuffer, region.bufferContent);
25357             } else {
25358               if (options.excludeFalseConflicts && isFalseConflict(region.aContent, region.bContent)) {
25359                 okBuffer.push.apply(okBuffer, region.aContent);
25360               } else {
25361                 flushOk();
25362                 results.push({
25363                   conflict: {
25364                     a: region.aContent,
25365                     aIndex: region.aStart,
25366                     o: region.oContent,
25367                     oIndex: region.oStart,
25368                     b: region.bContent,
25369                     bIndex: region.bStart
25370                   }
25371                 });
25372               }
25373             }
25374           });
25375
25376           flushOk();
25377           return results;
25378         }
25379
25380         function actionMergeRemoteChanges(id, localGraph, remoteGraph, discardTags, formatUser) {
25381             discardTags = discardTags || {};
25382             var _option = 'safe';  // 'safe', 'force_local', 'force_remote'
25383             var _conflicts = [];
25384
25385
25386             function user(d) {
25387                 return (typeof formatUser === 'function') ? formatUser(d) : d;
25388             }
25389
25390
25391             function mergeLocation(remote, target) {
25392                 function pointEqual(a, b) {
25393                     var epsilon = 1e-6;
25394                     return (Math.abs(a[0] - b[0]) < epsilon) && (Math.abs(a[1] - b[1]) < epsilon);
25395                 }
25396
25397                 if (_option === 'force_local' || pointEqual(target.loc, remote.loc)) {
25398                     return target;
25399                 }
25400                 if (_option === 'force_remote') {
25401                     return target.update({loc: remote.loc});
25402                 }
25403
25404                 _conflicts.push(_t('merge_remote_changes.conflict.location', { user: user(remote.user) }));
25405                 return target;
25406             }
25407
25408
25409             function mergeNodes(base, remote, target) {
25410                 if (_option === 'force_local' || fastDeepEqual(target.nodes, remote.nodes)) {
25411                     return target;
25412                 }
25413                 if (_option === 'force_remote') {
25414                     return target.update({nodes: remote.nodes});
25415                 }
25416
25417                 var ccount = _conflicts.length;
25418                 var o = base.nodes || [];
25419                 var a = target.nodes || [];
25420                 var b = remote.nodes || [];
25421                 var nodes = [];
25422                 var hunks = diff3Merge(a, o, b, { excludeFalseConflicts: true });
25423
25424                 for (var i = 0; i < hunks.length; i++) {
25425                     var hunk = hunks[i];
25426                     if (hunk.ok) {
25427                         nodes.push.apply(nodes, hunk.ok);
25428                     } else {
25429                         // for all conflicts, we can assume c.a !== c.b
25430                         // because `diff3Merge` called with `true` option to exclude false conflicts..
25431                         var c = hunk.conflict;
25432                         if (fastDeepEqual(c.o, c.a)) {  // only changed remotely
25433                             nodes.push.apply(nodes, c.b);
25434                         } else if (fastDeepEqual(c.o, c.b)) {  // only changed locally
25435                             nodes.push.apply(nodes, c.a);
25436                         } else {       // changed both locally and remotely
25437                             _conflicts.push(_t('merge_remote_changes.conflict.nodelist', { user: user(remote.user) }));
25438                             break;
25439                         }
25440                     }
25441                 }
25442
25443                 return (_conflicts.length === ccount) ? target.update({nodes: nodes}) : target;
25444             }
25445
25446
25447             function mergeChildren(targetWay, children, updates, graph) {
25448                 function isUsed(node, targetWay) {
25449                     var hasInterestingParent = graph.parentWays(node)
25450                         .some(function(way) { return way.id !== targetWay.id; });
25451
25452                     return node.hasInterestingTags() ||
25453                         hasInterestingParent ||
25454                         graph.parentRelations(node).length > 0;
25455                 }
25456
25457                 var ccount = _conflicts.length;
25458
25459                 for (var i = 0; i < children.length; i++) {
25460                     var id = children[i];
25461                     var node = graph.hasEntity(id);
25462
25463                     // remove unused childNodes..
25464                     if (targetWay.nodes.indexOf(id) === -1) {
25465                         if (node && !isUsed(node, targetWay)) {
25466                             updates.removeIds.push(id);
25467                         }
25468                         continue;
25469                     }
25470
25471                     // restore used childNodes..
25472                     var local = localGraph.hasEntity(id);
25473                     var remote = remoteGraph.hasEntity(id);
25474                     var target;
25475
25476                     if (_option === 'force_remote' && remote && remote.visible) {
25477                         updates.replacements.push(remote);
25478
25479                     } else if (_option === 'force_local' && local) {
25480                         target = osmEntity(local);
25481                         if (remote) {
25482                             target = target.update({ version: remote.version });
25483                         }
25484                         updates.replacements.push(target);
25485
25486                     } else if (_option === 'safe' && local && remote && local.version !== remote.version) {
25487                         target = osmEntity(local, { version: remote.version });
25488                         if (remote.visible) {
25489                             target = mergeLocation(remote, target);
25490                         } else {
25491                             _conflicts.push(_t('merge_remote_changes.conflict.deleted', { user: user(remote.user) }));
25492                         }
25493
25494                         if (_conflicts.length !== ccount) { break; }
25495                         updates.replacements.push(target);
25496                     }
25497                 }
25498
25499                 return targetWay;
25500             }
25501
25502
25503             function updateChildren(updates, graph) {
25504                 for (var i = 0; i < updates.replacements.length; i++) {
25505                     graph = graph.replace(updates.replacements[i]);
25506                 }
25507                 if (updates.removeIds.length) {
25508                     graph = actionDeleteMultiple(updates.removeIds)(graph);
25509                 }
25510                 return graph;
25511             }
25512
25513
25514             function mergeMembers(remote, target) {
25515                 if (_option === 'force_local' || fastDeepEqual(target.members, remote.members)) {
25516                     return target;
25517                 }
25518                 if (_option === 'force_remote') {
25519                     return target.update({members: remote.members});
25520                 }
25521
25522                 _conflicts.push(_t('merge_remote_changes.conflict.memberlist', { user: user(remote.user) }));
25523                 return target;
25524             }
25525
25526
25527             function mergeTags(base, remote, target) {
25528                 if (_option === 'force_local' || fastDeepEqual(target.tags, remote.tags)) {
25529                     return target;
25530                 }
25531                 if (_option === 'force_remote') {
25532                     return target.update({tags: remote.tags});
25533                 }
25534
25535                 var ccount = _conflicts.length;
25536                 var o = base.tags || {};
25537                 var a = target.tags || {};
25538                 var b = remote.tags || {};
25539                 var keys = utilArrayUnion(utilArrayUnion(Object.keys(o), Object.keys(a)), Object.keys(b))
25540                     .filter(function(k) { return !discardTags[k]; });
25541                 var tags = Object.assign({}, a);   // shallow copy
25542                 var changed = false;
25543
25544                 for (var i = 0; i < keys.length; i++) {
25545                     var k = keys[i];
25546
25547                     if (o[k] !== b[k] && a[k] !== b[k]) {    // changed remotely..
25548                         if (o[k] !== a[k]) {      // changed locally..
25549                             _conflicts.push(_t('merge_remote_changes.conflict.tags',
25550                                 { tag: k, local: a[k], remote: b[k], user: user(remote.user) }));
25551
25552                         } else {                  // unchanged locally, accept remote change..
25553                             if (b.hasOwnProperty(k)) {
25554                                 tags[k] = b[k];
25555                             } else {
25556                                 delete tags[k];
25557                             }
25558                             changed = true;
25559                         }
25560                     }
25561                 }
25562
25563                 return (changed && _conflicts.length === ccount) ? target.update({tags: tags}) : target;
25564             }
25565
25566
25567             //  `graph.base()` is the common ancestor of the two graphs.
25568             //  `localGraph` contains user's edits up to saving
25569             //  `remoteGraph` contains remote edits to modified nodes
25570             //  `graph` must be a descendent of `localGraph` and may include
25571             //      some conflict resolution actions performed on it.
25572             //
25573             //                  --- ... --- `localGraph` -- ... -- `graph`
25574             //                 /
25575             //  `graph.base()` --- ... --- `remoteGraph`
25576             //
25577             var action = function(graph) {
25578                 var updates = { replacements: [], removeIds: [] };
25579                 var base = graph.base().entities[id];
25580                 var local = localGraph.entity(id);
25581                 var remote = remoteGraph.entity(id);
25582                 var target = osmEntity(local, { version: remote.version });
25583
25584                 // delete/undelete
25585                 if (!remote.visible) {
25586                     if (_option === 'force_remote') {
25587                         return actionDeleteMultiple([id])(graph);
25588
25589                     } else if (_option === 'force_local') {
25590                         if (target.type === 'way') {
25591                             target = mergeChildren(target, utilArrayUniq(local.nodes), updates, graph);
25592                             graph = updateChildren(updates, graph);
25593                         }
25594                         return graph.replace(target);
25595
25596                     } else {
25597                         _conflicts.push(_t('merge_remote_changes.conflict.deleted', { user: user(remote.user) }));
25598                         return graph;  // do nothing
25599                     }
25600                 }
25601
25602                 // merge
25603                 if (target.type === 'node') {
25604                     target = mergeLocation(remote, target);
25605
25606                 } else if (target.type === 'way') {
25607                     // pull in any child nodes that may not be present locally..
25608                     graph.rebase(remoteGraph.childNodes(remote), [graph], false);
25609                     target = mergeNodes(base, remote, target);
25610                     target = mergeChildren(target, utilArrayUnion(local.nodes, remote.nodes), updates, graph);
25611
25612                 } else if (target.type === 'relation') {
25613                     target = mergeMembers(remote, target);
25614                 }
25615
25616                 target = mergeTags(base, remote, target);
25617
25618                 if (!_conflicts.length) {
25619                     graph = updateChildren(updates, graph).replace(target);
25620                 }
25621
25622                 return graph;
25623             };
25624
25625
25626             action.withOption = function(opt) {
25627                 _option = opt;
25628                 return action;
25629             };
25630
25631
25632             action.conflicts = function() {
25633                 return _conflicts;
25634             };
25635
25636
25637             return action;
25638         }
25639
25640         // https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/command/MoveCommand.java
25641         // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MoveNodeAction.as
25642         function actionMove(moveIDs, tryDelta, projection, cache) {
25643             var _delta = tryDelta;
25644
25645             function setupCache(graph) {
25646                 function canMove(nodeID) {
25647                     // Allow movement of any node that is in the selectedIDs list..
25648                     if (moveIDs.indexOf(nodeID) !== -1) { return true; }
25649
25650                     // Allow movement of a vertex where 2 ways meet..
25651                     var parents = graph.parentWays(graph.entity(nodeID));
25652                     if (parents.length < 3) { return true; }
25653
25654                     // Restrict movement of a vertex where >2 ways meet, unless all parentWays are moving too..
25655                     var parentsMoving = parents.every(function(way) { return cache.moving[way.id]; });
25656                     if (!parentsMoving) { delete cache.moving[nodeID]; }
25657
25658                     return parentsMoving;
25659                 }
25660
25661                 function cacheEntities(ids) {
25662                     for (var i = 0; i < ids.length; i++) {
25663                         var id = ids[i];
25664                         if (cache.moving[id]) { continue; }
25665                         cache.moving[id] = true;
25666
25667                         var entity = graph.hasEntity(id);
25668                         if (!entity) { continue; }
25669
25670                         if (entity.type === 'node') {
25671                             cache.nodes.push(id);
25672                             cache.startLoc[id] = entity.loc;
25673                         } else if (entity.type === 'way') {
25674                             cache.ways.push(id);
25675                             cacheEntities(entity.nodes);
25676                         } else {
25677                             cacheEntities(entity.members.map(function(member) {
25678                                 return member.id;
25679                             }));
25680                         }
25681                     }
25682                 }
25683
25684                 function cacheIntersections(ids) {
25685                     function isEndpoint(way, id) {
25686                         return !way.isClosed() && !!way.affix(id);
25687                     }
25688
25689                     for (var i = 0; i < ids.length; i++) {
25690                         var id = ids[i];
25691
25692                         // consider only intersections with 1 moved and 1 unmoved way.
25693                         var childNodes = graph.childNodes(graph.entity(id));
25694                         for (var j = 0; j < childNodes.length; j++) {
25695                             var node = childNodes[j];
25696                             var parents = graph.parentWays(node);
25697                             if (parents.length !== 2) { continue; }
25698
25699                             var moved = graph.entity(id);
25700                             var unmoved = null;
25701                             for (var k = 0; k < parents.length; k++) {
25702                                 var way = parents[k];
25703                                 if (!cache.moving[way.id]) {
25704                                     unmoved = way;
25705                                     break;
25706                                 }
25707                             }
25708                             if (!unmoved) { continue; }
25709
25710                             // exclude ways that are overly connected..
25711                             if (utilArrayIntersection(moved.nodes, unmoved.nodes).length > 2) { continue; }
25712                             if (moved.isArea() || unmoved.isArea()) { continue; }
25713
25714                             cache.intersections.push({
25715                                 nodeId: node.id,
25716                                 movedId: moved.id,
25717                                 unmovedId: unmoved.id,
25718                                 movedIsEP: isEndpoint(moved, node.id),
25719                                 unmovedIsEP: isEndpoint(unmoved, node.id)
25720                             });
25721                         }
25722                     }
25723                 }
25724
25725
25726                 if (!cache) {
25727                     cache = {};
25728                 }
25729                 if (!cache.ok) {
25730                     cache.moving = {};
25731                     cache.intersections = [];
25732                     cache.replacedVertex = {};
25733                     cache.startLoc = {};
25734                     cache.nodes = [];
25735                     cache.ways = [];
25736
25737                     cacheEntities(moveIDs);
25738                     cacheIntersections(cache.ways);
25739                     cache.nodes = cache.nodes.filter(canMove);
25740
25741                     cache.ok = true;
25742                 }
25743             }
25744
25745
25746             // Place a vertex where the moved vertex used to be, to preserve way shape..
25747             //
25748             //  Start:
25749             //      b ---- e
25750             //     / \
25751             //    /   \
25752             //   /     \
25753             //  a       c
25754             //
25755             //      *               node '*' added to preserve shape
25756             //     / \
25757             //    /   b ---- e      way `b,e` moved here:
25758             //   /     \
25759             //  a       c
25760             //
25761             //
25762             function replaceMovedVertex(nodeId, wayId, graph, delta) {
25763                 var way = graph.entity(wayId);
25764                 var moved = graph.entity(nodeId);
25765                 var movedIndex = way.nodes.indexOf(nodeId);
25766                 var len, prevIndex, nextIndex;
25767
25768                 if (way.isClosed()) {
25769                     len = way.nodes.length - 1;
25770                     prevIndex = (movedIndex + len - 1) % len;
25771                     nextIndex = (movedIndex + len + 1) % len;
25772                 } else {
25773                     len = way.nodes.length;
25774                     prevIndex = movedIndex - 1;
25775                     nextIndex = movedIndex + 1;
25776                 }
25777
25778                 var prev = graph.hasEntity(way.nodes[prevIndex]);
25779                 var next = graph.hasEntity(way.nodes[nextIndex]);
25780
25781                 // Don't add orig vertex at endpoint..
25782                 if (!prev || !next) { return graph; }
25783
25784                 var key = wayId + '_' + nodeId;
25785                 var orig = cache.replacedVertex[key];
25786                 if (!orig) {
25787                     orig = osmNode();
25788                     cache.replacedVertex[key] = orig;
25789                     cache.startLoc[orig.id] = cache.startLoc[nodeId];
25790                 }
25791
25792                 var start, end;
25793                 if (delta) {
25794                     start = projection(cache.startLoc[nodeId]);
25795                     end = projection.invert(geoVecAdd(start, delta));
25796                 } else {
25797                     end = cache.startLoc[nodeId];
25798                 }
25799                 orig = orig.move(end);
25800
25801                 var angle = Math.abs(geoAngle(orig, prev, projection) -
25802                         geoAngle(orig, next, projection)) * 180 / Math.PI;
25803
25804                 // Don't add orig vertex if it would just make a straight line..
25805                 if (angle > 175 && angle < 185) { return graph; }
25806
25807                 // moving forward or backward along way?
25808                 var p1 = [prev.loc, orig.loc, moved.loc, next.loc].map(projection);
25809                 var p2 = [prev.loc, moved.loc, orig.loc, next.loc].map(projection);
25810                 var d1 = geoPathLength(p1);
25811                 var d2 = geoPathLength(p2);
25812                 var insertAt = (d1 <= d2) ? movedIndex : nextIndex;
25813
25814                 // moving around closed loop?
25815                 if (way.isClosed() && insertAt === 0) { insertAt = len; }
25816
25817                 way = way.addNode(orig.id, insertAt);
25818                 return graph.replace(orig).replace(way);
25819             }
25820
25821
25822             // Remove duplicate vertex that might have been added by
25823             // replaceMovedVertex.  This is done after the unzorro checks.
25824             function removeDuplicateVertices(wayId, graph) {
25825                 var way = graph.entity(wayId);
25826                 var epsilon = 1e-6;
25827                 var prev, curr;
25828
25829                 function isInteresting(node, graph) {
25830                     return graph.parentWays(node).length > 1 ||
25831                         graph.parentRelations(node).length ||
25832                         node.hasInterestingTags();
25833                 }
25834
25835                 for (var i = 0; i < way.nodes.length; i++) {
25836                     curr = graph.entity(way.nodes[i]);
25837
25838                     if (prev && curr && geoVecEqual(prev.loc, curr.loc, epsilon)) {
25839                         if (!isInteresting(prev, graph)) {
25840                             way = way.removeNode(prev.id);
25841                             graph = graph.replace(way).remove(prev);
25842                         } else if (!isInteresting(curr, graph)) {
25843                             way = way.removeNode(curr.id);
25844                             graph = graph.replace(way).remove(curr);
25845                         }
25846                     }
25847
25848                     prev = curr;
25849                 }
25850
25851                 return graph;
25852             }
25853
25854
25855             // Reorder nodes around intersections that have moved..
25856             //
25857             //  Start:                way1.nodes: b,e         (moving)
25858             //  a - b - c ----- d     way2.nodes: a,b,c,d     (static)
25859             //      |                 vertex: b
25860             //      e                 isEP1: true,  isEP2, false
25861             //
25862             //  way1 `b,e` moved here:
25863             //  a ----- c = b - d
25864             //              |
25865             //              e
25866             //
25867             //  reorder nodes         way1.nodes: b,e
25868             //  a ----- c - b - d     way2.nodes: a,c,b,d
25869             //              |
25870             //              e
25871             //
25872             function unZorroIntersection(intersection, graph) {
25873                 var vertex = graph.entity(intersection.nodeId);
25874                 var way1 = graph.entity(intersection.movedId);
25875                 var way2 = graph.entity(intersection.unmovedId);
25876                 var isEP1 = intersection.movedIsEP;
25877                 var isEP2 = intersection.unmovedIsEP;
25878
25879                 // don't move the vertex if it is the endpoint of both ways.
25880                 if (isEP1 && isEP2) { return graph; }
25881
25882                 var nodes1 = graph.childNodes(way1).filter(function(n) { return n !== vertex; });
25883                 var nodes2 = graph.childNodes(way2).filter(function(n) { return n !== vertex; });
25884
25885                 if (way1.isClosed() && way1.first() === vertex.id) { nodes1.push(nodes1[0]); }
25886                 if (way2.isClosed() && way2.first() === vertex.id) { nodes2.push(nodes2[0]); }
25887
25888                 var edge1 = !isEP1 && geoChooseEdge(nodes1, projection(vertex.loc), projection);
25889                 var edge2 = !isEP2 && geoChooseEdge(nodes2, projection(vertex.loc), projection);
25890                 var loc;
25891
25892                 // snap vertex to nearest edge (or some point between them)..
25893                 if (!isEP1 && !isEP2) {
25894                     var epsilon = 1e-6, maxIter = 10;
25895                     for (var i = 0; i < maxIter; i++) {
25896                         loc = geoVecInterp(edge1.loc, edge2.loc, 0.5);
25897                         edge1 = geoChooseEdge(nodes1, projection(loc), projection);
25898                         edge2 = geoChooseEdge(nodes2, projection(loc), projection);
25899                         if (Math.abs(edge1.distance - edge2.distance) < epsilon) { break; }
25900                     }
25901                 } else if (!isEP1) {
25902                     loc = edge1.loc;
25903                 } else {
25904                     loc = edge2.loc;
25905                 }
25906
25907                 graph = graph.replace(vertex.move(loc));
25908
25909                 // if zorro happened, reorder nodes..
25910                 if (!isEP1 && edge1.index !== way1.nodes.indexOf(vertex.id)) {
25911                     way1 = way1.removeNode(vertex.id).addNode(vertex.id, edge1.index);
25912                     graph = graph.replace(way1);
25913                 }
25914                 if (!isEP2 && edge2.index !== way2.nodes.indexOf(vertex.id)) {
25915                     way2 = way2.removeNode(vertex.id).addNode(vertex.id, edge2.index);
25916                     graph = graph.replace(way2);
25917                 }
25918
25919                 return graph;
25920             }
25921
25922
25923             function cleanupIntersections(graph) {
25924                 for (var i = 0; i < cache.intersections.length; i++) {
25925                     var obj = cache.intersections[i];
25926                     graph = replaceMovedVertex(obj.nodeId, obj.movedId, graph, _delta);
25927                     graph = replaceMovedVertex(obj.nodeId, obj.unmovedId, graph, null);
25928                     graph = unZorroIntersection(obj, graph);
25929                     graph = removeDuplicateVertices(obj.movedId, graph);
25930                     graph = removeDuplicateVertices(obj.unmovedId, graph);
25931                 }
25932
25933                 return graph;
25934             }
25935
25936
25937             // check if moving way endpoint can cross an unmoved way, if so limit delta..
25938             function limitDelta(graph) {
25939                 function moveNode(loc) {
25940                     return geoVecAdd(projection(loc), _delta);
25941                 }
25942
25943                 for (var i = 0; i < cache.intersections.length; i++) {
25944                     var obj = cache.intersections[i];
25945
25946                     // Don't limit movement if this is vertex joins 2 endpoints..
25947                     if (obj.movedIsEP && obj.unmovedIsEP) { continue; }
25948                     // Don't limit movement if this vertex is not an endpoint anyway..
25949                     if (!obj.movedIsEP) { continue; }
25950
25951                     var node = graph.entity(obj.nodeId);
25952                     var start = projection(node.loc);
25953                     var end = geoVecAdd(start, _delta);
25954                     var movedNodes = graph.childNodes(graph.entity(obj.movedId));
25955                     var movedPath = movedNodes.map(function(n) { return moveNode(n.loc); });
25956                     var unmovedNodes = graph.childNodes(graph.entity(obj.unmovedId));
25957                     var unmovedPath = unmovedNodes.map(function(n) { return projection(n.loc); });
25958                     var hits = geoPathIntersections(movedPath, unmovedPath);
25959
25960                     for (var j = 0; i < hits.length; i++) {
25961                         if (geoVecEqual(hits[j], end)) { continue; }
25962                         var edge = geoChooseEdge(unmovedNodes, end, projection);
25963                         _delta = geoVecSubtract(projection(edge.loc), start);
25964                     }
25965                 }
25966             }
25967
25968
25969             var action = function(graph) {
25970                 if (_delta[0] === 0 && _delta[1] === 0) { return graph; }
25971
25972                 setupCache(graph);
25973
25974                 if (cache.intersections.length) {
25975                     limitDelta(graph);
25976                 }
25977
25978                 for (var i = 0; i < cache.nodes.length; i++) {
25979                     var node = graph.entity(cache.nodes[i]);
25980                     var start = projection(node.loc);
25981                     var end = geoVecAdd(start, _delta);
25982                     graph = graph.replace(node.move(projection.invert(end)));
25983                 }
25984
25985                 if (cache.intersections.length) {
25986                     graph = cleanupIntersections(graph);
25987                 }
25988
25989                 return graph;
25990             };
25991
25992
25993             action.delta = function() {
25994                 return _delta;
25995             };
25996
25997
25998             return action;
25999         }
26000
26001         function actionMoveMember(relationId, fromIndex, toIndex) {
26002             return function(graph) {
26003                 return graph.replace(graph.entity(relationId).moveMember(fromIndex, toIndex));
26004             };
26005         }
26006
26007         function actionMoveNode(nodeID, toLoc) {
26008
26009             var action = function(graph, t) {
26010                 if (t === null || !isFinite(t)) { t = 1; }
26011                 t = Math.min(Math.max(+t, 0), 1);
26012
26013                 var node = graph.entity(nodeID);
26014                 return graph.replace(
26015                     node.move(geoVecInterp(node.loc, toLoc, t))
26016                 );
26017             };
26018
26019             action.transitionable = true;
26020
26021             return action;
26022         }
26023
26024         function actionNoop() {
26025             return function(graph) {
26026                 return graph;
26027             };
26028         }
26029
26030         function actionOrthogonalize(wayID, projection, vertexID, degThresh, ep) {
26031             var epsilon = ep || 1e-4;
26032             var threshold = degThresh || 13;  // degrees within right or straight to alter
26033
26034             // We test normalized dot products so we can compare as cos(angle)
26035             var lowerThreshold = Math.cos((90 - threshold) * Math.PI / 180);
26036             var upperThreshold = Math.cos(threshold * Math.PI / 180);
26037
26038
26039             var action = function(graph, t) {
26040                 if (t === null || !isFinite(t)) { t = 1; }
26041                 t = Math.min(Math.max(+t, 0), 1);
26042
26043                 var way = graph.entity(wayID);
26044                 way = way.removeNode('');   // sanity check - remove any consecutive duplicates
26045
26046                 if (way.tags.nonsquare) {
26047                     var tags = Object.assign({}, way.tags);
26048                     // since we're squaring, remove indication that this is physically unsquare
26049                     delete tags.nonsquare;
26050                     way = way.update({tags: tags});
26051                 }
26052
26053                 graph = graph.replace(way);
26054
26055                 var isClosed = way.isClosed();
26056                 var nodes = graph.childNodes(way).slice();  // shallow copy
26057                 if (isClosed) { nodes.pop(); }
26058
26059                 if (vertexID !== undefined) {
26060                     nodes = nodeSubset(nodes, vertexID, isClosed);
26061                     if (nodes.length !== 3) { return graph; }
26062                 }
26063
26064                 // note: all geometry functions here use the unclosed node/point/coord list
26065
26066                 var nodeCount = {};
26067                 var points = [];
26068                 var corner = { i: 0, dotp: 1 };
26069                 var node, point, loc, score, motions, i, j;
26070
26071                 for (i = 0; i < nodes.length; i++) {
26072                     node = nodes[i];
26073                     nodeCount[node.id] = (nodeCount[node.id] || 0) + 1;
26074                     points.push({ id: node.id, coord: projection(node.loc) });
26075                 }
26076
26077
26078                 if (points.length === 3) {   // move only one vertex for right triangle
26079                     for (i = 0; i < 1000; i++) {
26080                         motions = points.map(calcMotion);
26081
26082                         points[corner.i].coord = geoVecAdd(points[corner.i].coord, motions[corner.i]);
26083                         score = corner.dotp;
26084                         if (score < epsilon) {
26085                             break;
26086                         }
26087                     }
26088
26089                     node = graph.entity(nodes[corner.i].id);
26090                     loc = projection.invert(points[corner.i].coord);
26091                     graph = graph.replace(node.move(geoVecInterp(node.loc, loc, t)));
26092
26093                 } else {
26094                     var straights = [];
26095                     var simplified = [];
26096
26097                     // Remove points from nearly straight sections..
26098                     // This produces a simplified shape to orthogonalize
26099                     for (i = 0; i < points.length; i++) {
26100                         point = points[i];
26101                         var dotp = 0;
26102                         if (isClosed || (i > 0 && i < points.length - 1)) {
26103                             var a = points[(i - 1 + points.length) % points.length];
26104                             var b = points[(i + 1) % points.length];
26105                             dotp = Math.abs(geoOrthoNormalizedDotProduct(a.coord, b.coord, point.coord));
26106                         }
26107
26108                         if (dotp > upperThreshold) {
26109                             straights.push(point);
26110                         } else {
26111                             simplified.push(point);
26112                         }
26113                     }
26114
26115                     // Orthogonalize the simplified shape
26116                     var bestPoints = clonePoints(simplified);
26117                     var originalPoints = clonePoints(simplified);
26118
26119                     score = Infinity;
26120                     for (i = 0; i < 1000; i++) {
26121                         motions = simplified.map(calcMotion);
26122
26123                         for (j = 0; j < motions.length; j++) {
26124                             simplified[j].coord = geoVecAdd(simplified[j].coord, motions[j]);
26125                         }
26126                         var newScore = geoOrthoCalcScore(simplified, isClosed, epsilon, threshold);
26127                         if (newScore < score) {
26128                             bestPoints = clonePoints(simplified);
26129                             score = newScore;
26130                         }
26131                         if (score < epsilon) {
26132                             break;
26133                         }
26134                     }
26135
26136                     var bestCoords = bestPoints.map(function(p) { return p.coord; });
26137                     if (isClosed) { bestCoords.push(bestCoords[0]); }
26138
26139                     // move the nodes that should move
26140                     for (i = 0; i < bestPoints.length; i++) {
26141                         point = bestPoints[i];
26142                         if (!geoVecEqual(originalPoints[i].coord, point.coord)) {
26143                             node = graph.entity(point.id);
26144                             loc = projection.invert(point.coord);
26145                             graph = graph.replace(node.move(geoVecInterp(node.loc, loc, t)));
26146                         }
26147                     }
26148
26149                     // move the nodes along straight segments
26150                     for (i = 0; i < straights.length; i++) {
26151                         point = straights[i];
26152                         if (nodeCount[point.id] > 1) { continue; }   // skip self-intersections
26153
26154                         node = graph.entity(point.id);
26155
26156                         if (t === 1 &&
26157                             graph.parentWays(node).length === 1 &&
26158                             graph.parentRelations(node).length === 0 &&
26159                             !node.hasInterestingTags()
26160                         ) {
26161                             // remove uninteresting points..
26162                             graph = actionDeleteNode(node.id)(graph);
26163
26164                         } else {
26165                             // move interesting points to the nearest edge..
26166                             var choice = geoVecProject(point.coord, bestCoords);
26167                             if (choice) {
26168                                 loc = projection.invert(choice.target);
26169                                 graph = graph.replace(node.move(geoVecInterp(node.loc, loc, t)));
26170                             }
26171                         }
26172                     }
26173                 }
26174
26175                 return graph;
26176
26177
26178                 function clonePoints(array) {
26179                     return array.map(function(p) {
26180                         return { id: p.id, coord: [p.coord[0], p.coord[1]] };
26181                     });
26182                 }
26183
26184
26185                 function calcMotion(point, i, array) {
26186                     // don't try to move the endpoints of a non-closed way.
26187                     if (!isClosed && (i === 0 || i === array.length - 1)) { return [0, 0]; }
26188                     // don't try to move a node that appears more than once (self intersection)
26189                     if (nodeCount[array[i].id] > 1) { return [0, 0]; }
26190
26191                     var a = array[(i - 1 + array.length) % array.length].coord;
26192                     var origin = point.coord;
26193                     var b = array[(i + 1) % array.length].coord;
26194                     var p = geoVecSubtract(a, origin);
26195                     var q = geoVecSubtract(b, origin);
26196
26197                     var scale = 2 * Math.min(geoVecLength(p), geoVecLength(q));
26198                     p = geoVecNormalize(p);
26199                     q = geoVecNormalize(q);
26200
26201                     var dotp = (p[0] * q[0] + p[1] * q[1]);
26202                     var val = Math.abs(dotp);
26203
26204                     if (val < lowerThreshold) {  // nearly orthogonal
26205                         corner.i = i;
26206                         corner.dotp = val;
26207                         var vec = geoVecNormalize(geoVecAdd(p, q));
26208                         return geoVecScale(vec, 0.1 * dotp * scale);
26209                     }
26210
26211                     return [0, 0];   // do nothing
26212                 }
26213             };
26214
26215
26216             // if we are only orthogonalizing one vertex,
26217             // get that vertex and the previous and next
26218             function nodeSubset(nodes, vertexID, isClosed) {
26219                 var first = isClosed ? 0 : 1;
26220                 var last = isClosed ? nodes.length : nodes.length - 1;
26221
26222                 for (var i = first; i < last; i++) {
26223                     if (nodes[i].id === vertexID) {
26224                         return [
26225                             nodes[(i - 1 + nodes.length) % nodes.length],
26226                             nodes[i],
26227                             nodes[(i + 1) % nodes.length]
26228                         ];
26229                     }
26230                 }
26231
26232                 return [];
26233             }
26234
26235
26236             action.disabled = function(graph) {
26237                 var way = graph.entity(wayID);
26238                 way = way.removeNode('');  // sanity check - remove any consecutive duplicates
26239                 graph = graph.replace(way);
26240
26241                 var isClosed = way.isClosed();
26242                 var nodes = graph.childNodes(way).slice();  // shallow copy
26243                 if (isClosed) { nodes.pop(); }
26244
26245                 var allowStraightAngles = false;
26246                 if (vertexID !== undefined) {
26247                     allowStraightAngles = true;
26248                     nodes = nodeSubset(nodes, vertexID, isClosed);
26249                     if (nodes.length !== 3) { return 'end_vertex'; }
26250                 }
26251
26252                 var coords = nodes.map(function(n) { return projection(n.loc); });
26253                 var score = geoOrthoCanOrthogonalize(coords, isClosed, epsilon, threshold, allowStraightAngles);
26254
26255                 if (score === null) {
26256                     return 'not_squarish';
26257                 } else if (score === 0) {
26258                     return 'square_enough';
26259                 } else {
26260                     return false;
26261                 }
26262             };
26263
26264
26265             action.transitionable = true;
26266
26267             return action;
26268         }
26269
26270         // `actionRestrictTurn` creates a turn restriction relation.
26271         //
26272         // `turn` must be an `osmTurn` object
26273         // see osm/intersection.js, pathToTurn()
26274         //
26275         // This specifies a restriction of type `restriction` when traveling from
26276         // `turn.from.way` toward `turn.to.way` via `turn.via.node` OR `turn.via.ways`.
26277         // (The action does not check that these entities form a valid intersection.)
26278         //
26279         // From, to, and via ways should be split before calling this action.
26280         // (old versions of the code would split the ways here, but we no longer do it)
26281         //
26282         // For testing convenience, accepts a restrictionID to assign to the new
26283         // relation. Normally, this will be undefined and the relation will
26284         // automatically be assigned a new ID.
26285         //
26286         function actionRestrictTurn(turn, restrictionType, restrictionID) {
26287
26288             return function(graph) {
26289                 var fromWay = graph.entity(turn.from.way);
26290                 var toWay = graph.entity(turn.to.way);
26291                 var viaNode = turn.via.node && graph.entity(turn.via.node);
26292                 var viaWays = turn.via.ways && turn.via.ways.map(function(id) { return graph.entity(id); });
26293                 var members = [];
26294
26295                 members.push({ id: fromWay.id, type: 'way',  role: 'from' });
26296
26297                 if (viaNode) {
26298                     members.push({ id: viaNode.id,  type: 'node', role: 'via' });
26299                 } else if (viaWays) {
26300                     viaWays.forEach(function(viaWay) {
26301                         members.push({ id: viaWay.id,  type: 'way', role: 'via' });
26302                     });
26303                 }
26304
26305                 members.push({ id: toWay.id, type: 'way',  role: 'to' });
26306
26307                 return graph.replace(osmRelation({
26308                     id: restrictionID,
26309                     tags: {
26310                         type: 'restriction',
26311                         restriction: restrictionType
26312                     },
26313                     members: members
26314                 }));
26315             };
26316         }
26317
26318         function actionRevert(id) {
26319             var action = function(graph) {
26320                 var entity = graph.hasEntity(id),
26321                     base = graph.base().entities[id];
26322
26323                 if (entity && !base) {    // entity will be removed..
26324                     if (entity.type === 'node') {
26325                         graph.parentWays(entity)
26326                             .forEach(function(parent) {
26327                                 parent = parent.removeNode(id);
26328                                 graph = graph.replace(parent);
26329
26330                                 if (parent.isDegenerate()) {
26331                                     graph = actionDeleteWay(parent.id)(graph);
26332                                 }
26333                             });
26334                     }
26335
26336                     graph.parentRelations(entity)
26337                         .forEach(function(parent) {
26338                             parent = parent.removeMembersWithID(id);
26339                             graph = graph.replace(parent);
26340
26341                             if (parent.isDegenerate()) {
26342                                 graph = actionDeleteRelation(parent.id)(graph);
26343                             }
26344                         });
26345                 }
26346
26347                 return graph.revert(id);
26348             };
26349
26350             return action;
26351         }
26352
26353         function actionRotate(rotateIds, pivot, angle, projection) {
26354
26355             var action = function(graph) {
26356                 return graph.update(function(graph) {
26357                     utilGetAllNodes(rotateIds, graph).forEach(function(node) {
26358                         var point = geoRotate([projection(node.loc)], angle, pivot)[0];
26359                         graph = graph.replace(node.move(projection.invert(point)));
26360                     });
26361                 });
26362             };
26363
26364             return action;
26365         }
26366
26367         /* Align nodes along their common axis */
26368         function actionStraightenNodes(nodeIDs, projection) {
26369
26370             function positionAlongWay(a, o, b) {
26371                 return geoVecDot(a, b, o) / geoVecDot(b, b, o);
26372             }
26373
26374             // returns the endpoints of the long axis of symmetry of the `points` bounding rect 
26375             function getEndpoints(points) {
26376                 var ssr = geoGetSmallestSurroundingRectangle(points);
26377
26378                 // Choose line pq = axis of symmetry.
26379                 // The shape's surrounding rectangle has 2 axes of symmetry.
26380                 // Snap points to the long axis
26381                 var p1 = [(ssr.poly[0][0] + ssr.poly[1][0]) / 2, (ssr.poly[0][1] + ssr.poly[1][1]) / 2 ];
26382                 var q1 = [(ssr.poly[2][0] + ssr.poly[3][0]) / 2, (ssr.poly[2][1] + ssr.poly[3][1]) / 2 ];
26383                 var p2 = [(ssr.poly[3][0] + ssr.poly[4][0]) / 2, (ssr.poly[3][1] + ssr.poly[4][1]) / 2 ];
26384                 var q2 = [(ssr.poly[1][0] + ssr.poly[2][0]) / 2, (ssr.poly[1][1] + ssr.poly[2][1]) / 2 ];
26385
26386                 var isLong = (geoVecLength(p1, q1) > geoVecLength(p2, q2));
26387                 if (isLong) {
26388                     return [p1, q1];
26389                 }
26390                 return [p2, q2];
26391             }
26392
26393
26394             var action = function(graph, t) {
26395                 if (t === null || !isFinite(t)) { t = 1; }
26396                 t = Math.min(Math.max(+t, 0), 1);
26397
26398                 var nodes = nodeIDs.map(function(id) { return graph.entity(id); });
26399                 var points = nodes.map(function(n) { return projection(n.loc); });
26400                 var endpoints = getEndpoints(points);
26401                 var startPoint = endpoints[0];
26402                 var endPoint = endpoints[1];
26403
26404                 // Move points onto the line connecting the endpoints
26405                 for (var i = 0; i < points.length; i++) {
26406                     var node = nodes[i];
26407                     var point = points[i];
26408                     var u = positionAlongWay(point, startPoint, endPoint);
26409                     var point2 = geoVecInterp(startPoint, endPoint, u);
26410                     var loc2 = projection.invert(point2);
26411                     graph = graph.replace(node.move(geoVecInterp(node.loc, loc2, t)));
26412                 }
26413
26414                 return graph;
26415             };
26416
26417
26418             action.disabled = function(graph) {
26419
26420                 var nodes = nodeIDs.map(function(id) { return graph.entity(id); });
26421                 var points = nodes.map(function(n) { return projection(n.loc); });
26422                 var endpoints = getEndpoints(points);
26423                 var startPoint = endpoints[0];
26424                 var endPoint = endpoints[1];
26425
26426                 var maxDistance = 0;
26427
26428                 for (var i = 0; i < points.length; i++) {
26429                     var point = points[i];
26430                     var u = positionAlongWay(point, startPoint, endPoint);
26431                     var p = geoVecInterp(startPoint, endPoint, u);
26432                     var dist = geoVecLength(p, point);
26433
26434                     if (!isNaN(dist) && dist > maxDistance) {
26435                         maxDistance = dist;
26436                     }
26437                 }
26438
26439                 if (maxDistance < 0.0001) {
26440                     return 'straight_enough';
26441                 }
26442             };
26443
26444
26445             action.transitionable = true;
26446
26447
26448             return action;
26449         }
26450
26451         /*
26452          * Based on https://github.com/openstreetmap/potlatch2/net/systemeD/potlatch2/tools/Straighten.as
26453          */
26454         function actionStraightenWay(selectedIDs, projection) {
26455
26456             function positionAlongWay(a, o, b) {
26457                 return geoVecDot(a, b, o) / geoVecDot(b, b, o);
26458             }
26459
26460             // Return all selected ways as a continuous, ordered array of nodes
26461             function allNodes(graph) {
26462                 var nodes = [];
26463                 var startNodes = [];
26464                 var endNodes = [];
26465                 var remainingWays = [];
26466                 var selectedWays = selectedIDs.filter(function(w) {
26467                     return graph.entity(w).type === 'way';
26468                 });
26469                 var selectedNodes = selectedIDs.filter(function(n) {
26470                     return graph.entity(n).type === 'node';
26471                 });
26472
26473                 for (var i = 0; i < selectedWays.length; i++) {
26474                     var way = graph.entity(selectedWays[i]);
26475                     nodes = way.nodes.slice(0);
26476                     remainingWays.push(nodes);
26477                     startNodes.push(nodes[0]);
26478                     endNodes.push(nodes[nodes.length-1]);
26479                 }
26480
26481                 // Remove duplicate end/startNodes (duplicate nodes cannot be at the line end,
26482                 //   and need to be removed so currNode difference calculation below works)
26483                 // i.e. ["n-1", "n-1", "n-2"] => ["n-2"]
26484                 startNodes = startNodes.filter(function(n) {
26485                     return startNodes.indexOf(n) === startNodes.lastIndexOf(n);
26486                 });
26487                 endNodes = endNodes.filter(function(n) {
26488                     return endNodes.indexOf(n) === endNodes.lastIndexOf(n);
26489                 });
26490
26491                 // Choose the initial endpoint to start from
26492                 var currNode = utilArrayDifference(startNodes, endNodes)
26493                     .concat(utilArrayDifference(endNodes, startNodes))[0];
26494                 var nextWay = [];
26495                 nodes = [];
26496
26497                 // Create nested function outside of loop to avoid "function in loop" lint error
26498                 var getNextWay = function(currNode, remainingWays) {
26499                     return remainingWays.filter(function(way) {
26500                         return way[0] === currNode || way[way.length-1] === currNode;
26501                     })[0];
26502                 };
26503
26504                 // Add nodes to end of nodes array, until all ways are added
26505                 while (remainingWays.length) {
26506                     nextWay = getNextWay(currNode, remainingWays);
26507                     remainingWays = utilArrayDifference(remainingWays, [nextWay]);
26508
26509                     if (nextWay[0] !== currNode) {
26510                         nextWay.reverse();
26511                     }
26512                     nodes = nodes.concat(nextWay);
26513                     currNode = nodes[nodes.length-1];
26514                 }
26515
26516                 // If user selected 2 nodes to straighten between, then slice nodes array to those nodes
26517                 if (selectedNodes.length === 2) {
26518                     var startNodeIdx = nodes.indexOf(selectedNodes[0]);
26519                     var endNodeIdx = nodes.indexOf(selectedNodes[1]);
26520                     var sortedStartEnd = [startNodeIdx, endNodeIdx];
26521
26522                     sortedStartEnd.sort(function(a, b) { return a - b; });
26523                     nodes = nodes.slice(sortedStartEnd[0], sortedStartEnd[1]+1);
26524                 }
26525
26526                 return nodes.map(function(n) { return graph.entity(n); });
26527             }
26528
26529             function shouldKeepNode(node, graph) {
26530                 return graph.parentWays(node).length > 1 ||
26531                     graph.parentRelations(node).length ||
26532                     node.hasInterestingTags();
26533             }
26534
26535
26536             var action = function(graph, t) {
26537                 if (t === null || !isFinite(t)) { t = 1; }
26538                 t = Math.min(Math.max(+t, 0), 1);
26539
26540                 var nodes = allNodes(graph);
26541                 var points = nodes.map(function(n) { return projection(n.loc); });
26542                 var startPoint = points[0];
26543                 var endPoint = points[points.length-1];
26544                 var toDelete = [];
26545                 var i;
26546
26547                 for (i = 1; i < points.length-1; i++) {
26548                     var node = nodes[i];
26549                     var point = points[i];
26550
26551                     if (t < 1 || shouldKeepNode(node, graph)) {
26552                         var u = positionAlongWay(point, startPoint, endPoint);
26553                         var p = geoVecInterp(startPoint, endPoint, u);
26554                         var loc2 = projection.invert(p);
26555                         graph = graph.replace(node.move(geoVecInterp(node.loc, loc2, t)));
26556
26557                     } else {
26558                         // safe to delete
26559                         if (toDelete.indexOf(node) === -1) {
26560                             toDelete.push(node);
26561                         }
26562                     }
26563                 }
26564
26565                 for (i = 0; i < toDelete.length; i++) {
26566                     graph = actionDeleteNode(toDelete[i].id)(graph);
26567                 }
26568
26569                 return graph;
26570             };
26571
26572
26573             action.disabled = function(graph) {
26574                 // check way isn't too bendy
26575                 var nodes = allNodes(graph);
26576                 var points = nodes.map(function(n) { return projection(n.loc); });
26577                 var startPoint = points[0];
26578                 var endPoint = points[points.length-1];
26579                 var threshold = 0.2 * geoVecLength(startPoint, endPoint);
26580                 var i;
26581
26582                 if (threshold === 0) {
26583                     return 'too_bendy';
26584                 }
26585
26586                 var maxDistance = 0;
26587
26588                 for (i = 1; i < points.length - 1; i++) {
26589                     var point = points[i];
26590                     var u = positionAlongWay(point, startPoint, endPoint);
26591                     var p = geoVecInterp(startPoint, endPoint, u);
26592                     var dist = geoVecLength(p, point);
26593
26594                     // to bendy if point is off by 20% of total start/end distance in projected space
26595                     if (isNaN(dist) || dist > threshold) {
26596                         return 'too_bendy';
26597                     } else if (dist > maxDistance) {
26598                         maxDistance = dist;
26599                     }
26600                 }
26601
26602                 var keepingAllNodes = nodes.every(function(node, i) {
26603                     return i === 0 || i === nodes.length - 1 || shouldKeepNode(node, graph);
26604                 });
26605
26606                 if (maxDistance < 0.0001 &&
26607                     // Allow straightening even if already straight in order to remove extraneous nodes
26608                     keepingAllNodes) {
26609                     return 'straight_enough';
26610                 }
26611             };
26612
26613             action.transitionable = true;
26614
26615
26616             return action;
26617         }
26618
26619         // `actionUnrestrictTurn` deletes a turn restriction relation.
26620         //
26621         // `turn` must be an `osmTurn` object with a `restrictionID` property.
26622         // see osm/intersection.js, pathToTurn()
26623         //
26624         function actionUnrestrictTurn(turn) {
26625             return function(graph) {
26626                 return actionDeleteRelation(turn.restrictionID)(graph);
26627             };
26628         }
26629
26630         /* Reflect the given area around its axis of symmetry */
26631         function actionReflect(reflectIds, projection) {
26632             var _useLongAxis = true;
26633
26634
26635             var action = function(graph, t) {
26636                 if (t === null || !isFinite(t)) { t = 1; }
26637                 t = Math.min(Math.max(+t, 0), 1);
26638
26639                 var nodes = utilGetAllNodes(reflectIds, graph);
26640                 var points = nodes.map(function(n) { return projection(n.loc); });
26641                 var ssr = geoGetSmallestSurroundingRectangle(points);
26642
26643                 // Choose line pq = axis of symmetry.
26644                 // The shape's surrounding rectangle has 2 axes of symmetry.
26645                 // Reflect across the longer axis by default.
26646                 var p1 = [(ssr.poly[0][0] + ssr.poly[1][0]) / 2, (ssr.poly[0][1] + ssr.poly[1][1]) / 2 ];
26647                 var q1 = [(ssr.poly[2][0] + ssr.poly[3][0]) / 2, (ssr.poly[2][1] + ssr.poly[3][1]) / 2 ];
26648                 var p2 = [(ssr.poly[3][0] + ssr.poly[4][0]) / 2, (ssr.poly[3][1] + ssr.poly[4][1]) / 2 ];
26649                 var q2 = [(ssr.poly[1][0] + ssr.poly[2][0]) / 2, (ssr.poly[1][1] + ssr.poly[2][1]) / 2 ];
26650                 var p, q;
26651
26652                 var isLong = (geoVecLength(p1, q1) > geoVecLength(p2, q2));
26653                 if ((_useLongAxis && isLong) || (!_useLongAxis && !isLong)) {
26654                     p = p1;
26655                     q = q1;
26656                 } else {
26657                     p = p2;
26658                     q = q2;
26659                 }
26660
26661                 // reflect c across pq
26662                 // http://math.stackexchange.com/questions/65503/point-reflection-over-a-line
26663                 var dx = q[0] - p[0];
26664                 var dy = q[1] - p[1];
26665                 var a = (dx * dx - dy * dy) / (dx * dx + dy * dy);
26666                 var b = 2 * dx * dy / (dx * dx + dy * dy);
26667                 for (var i = 0; i < nodes.length; i++) {
26668                     var node = nodes[i];
26669                     var c = projection(node.loc);
26670                     var c2 = [
26671                         a * (c[0] - p[0]) + b * (c[1] - p[1]) + p[0],
26672                         b * (c[0] - p[0]) - a * (c[1] - p[1]) + p[1]
26673                     ];
26674                     var loc2 = projection.invert(c2);
26675                     node = node.move(geoVecInterp(node.loc, loc2, t));
26676                     graph = graph.replace(node);
26677                 }
26678
26679                 return graph;
26680             };
26681
26682
26683             action.useLongAxis = function(val) {
26684                 if (!arguments.length) { return _useLongAxis; }
26685                 _useLongAxis = val;
26686                 return action;
26687             };
26688
26689
26690             action.transitionable = true;
26691
26692
26693             return action;
26694         }
26695
26696         function actionUpgradeTags(entityId, oldTags, replaceTags) {
26697
26698             return function(graph) {
26699                 var entity = graph.entity(entityId);
26700                 var tags = Object.assign({}, entity.tags);  // shallow copy
26701                 var transferValue;
26702                 var semiIndex;
26703
26704                 for (var oldTagKey in oldTags) {
26705                     if (oldTags[oldTagKey] === '*') {
26706                         transferValue = tags[oldTagKey];
26707                         delete tags[oldTagKey];
26708                     } else {
26709                         var vals = tags[oldTagKey].split(';').filter(Boolean);
26710                         var oldIndex = vals.indexOf(oldTags[oldTagKey]);
26711                         if (vals.length === 1 || oldIndex === -1) {
26712                             delete tags[oldTagKey];
26713                         } else {
26714                             if (replaceTags && replaceTags[oldTagKey]) {
26715                                 // replacing a value within a semicolon-delimited value, note the index
26716                                 semiIndex = oldIndex;
26717                             }
26718                             vals.splice(oldIndex, 1);
26719                             tags[oldTagKey] = vals.join(';');
26720                         }
26721                     }
26722                 }
26723
26724                 if (replaceTags) {
26725                     for (var replaceKey in replaceTags) {
26726                         var replaceValue = replaceTags[replaceKey];
26727                         if (replaceValue === '*') {
26728                             if (tags[replaceKey] && tags[replaceKey] !== 'no') {
26729                                 // allow any pre-existing value except `no` (troll tag)
26730                                 continue;
26731                             } else {
26732                                 // otherwise assume `yes` is okay
26733                                 tags[replaceKey] = 'yes';
26734                             }
26735                         } else if (replaceValue === '$1') {
26736                             tags[replaceKey] = transferValue;
26737                         } else {
26738                             if (tags[replaceKey] && oldTags[replaceKey] && semiIndex !== undefined) {
26739                                 // don't override preexisting values
26740                                 var existingVals = tags[replaceKey].split(';').filter(Boolean);
26741                                 if (existingVals.indexOf(replaceValue) === -1) {
26742                                     existingVals.splice(semiIndex, 0, replaceValue);
26743                                     tags[replaceKey] = existingVals.join(';');
26744                                 }
26745                             } else {
26746                                 tags[replaceKey] = replaceValue;
26747                             }
26748                         }
26749                     }
26750                 }
26751
26752                 return graph.replace(entity.update({ tags: tags }));
26753             };
26754         }
26755
26756         function behaviorEdit(context) {
26757
26758             function behavior() {
26759                 context.map()
26760                     .minzoom(context.minEditableZoom());
26761             }
26762
26763
26764             behavior.off = function() {
26765                 context.map()
26766                     .minzoom(0);
26767             };
26768
26769             return behavior;
26770         }
26771
26772         /*
26773            The hover behavior adds the `.hover` class on pointerover to all elements to which
26774            the identical datum is bound, and removes it on pointerout.
26775
26776            The :hover pseudo-class is insufficient for iD's purposes because a datum's visual
26777            representation may consist of several elements scattered throughout the DOM hierarchy.
26778            Only one of these elements can have the :hover pseudo-class, but all of them will
26779            have the .hover class.
26780          */
26781         function behaviorHover(context) {
26782             var dispatch$1 = dispatch('hover');
26783             var _selection = select(null);
26784             var _newNodeId = null;
26785             var _initialNodeID = null;
26786             var _altDisables;
26787             var _ignoreVertex;
26788             var _targets = [];
26789
26790             // use pointer events on supported platforms; fallback to mouse events
26791             var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
26792
26793
26794             function keydown() {
26795                 if (_altDisables && event.keyCode === utilKeybinding.modifierCodes.alt) {
26796                     _selection.selectAll('.hover')
26797                         .classed('hover-suppressed', true)
26798                         .classed('hover', false);
26799
26800                     _selection
26801                         .classed('hover-disabled', true);
26802
26803                     dispatch$1.call('hover', this, null);
26804                 }
26805             }
26806
26807
26808             function keyup() {
26809                 if (_altDisables && event.keyCode === utilKeybinding.modifierCodes.alt) {
26810                     _selection.selectAll('.hover-suppressed')
26811                         .classed('hover-suppressed', false)
26812                         .classed('hover', true);
26813
26814                     _selection
26815                         .classed('hover-disabled', false);
26816
26817                     dispatch$1.call('hover', this, _targets);
26818                 }
26819             }
26820
26821
26822             function behavior(selection) {
26823                 _selection = selection;
26824
26825                 _targets = [];
26826
26827                 if (_initialNodeID) {
26828                     _newNodeId = _initialNodeID;
26829                     _initialNodeID = null;
26830                 } else {
26831                     _newNodeId = null;
26832                 }
26833
26834                 _selection
26835                     .on(_pointerPrefix + 'over.hover', pointerover)
26836                     .on(_pointerPrefix + 'out.hover', pointerout)
26837                     // treat pointerdown as pointerover for touch devices
26838                     .on(_pointerPrefix + 'down.hover', pointerover);
26839
26840                 select(window)
26841                     .on(_pointerPrefix + 'up.hover pointercancel.hover', pointerout, true)
26842                     .on('keydown.hover', keydown)
26843                     .on('keyup.hover', keyup);
26844
26845
26846                 function eventTarget() {
26847                     var datum = event.target && event.target.__data__;
26848                     if (typeof datum !== 'object') { return null; }
26849                     if (!(datum instanceof osmEntity) && datum.properties && (datum.properties.entity instanceof osmEntity)) {
26850                         return datum.properties.entity;
26851                     }
26852                     return datum;
26853                 }
26854
26855                 function pointerover() {
26856                     // ignore mouse hovers with buttons pressed unless dragging
26857                     if (context.mode().id.indexOf('drag') === -1 &&
26858                         (!event.pointerType || event.pointerType === 'mouse') &&
26859                         event.buttons) { return; }
26860
26861                     var target = eventTarget();
26862                     if (target && _targets.indexOf(target) === -1) {
26863                         _targets.push(target);
26864                         updateHover(_targets);
26865                     }
26866                 }
26867
26868                 function pointerout() {
26869
26870                     var target = eventTarget();
26871                     var index = _targets.indexOf(target);
26872                     if (index !== -1) {
26873                         _targets.splice(index);
26874                         updateHover(_targets);
26875                     }
26876                 }
26877
26878                 function allowsVertex(d) {
26879                     return d.geometry(context.graph()) === 'vertex' || _mainPresetIndex.allowsVertex(d, context.graph());
26880                 }
26881
26882                 function modeAllowsHover(target) {
26883                     var mode = context.mode();
26884                     if (mode.id === 'add-point') {
26885                         return mode.preset.matchGeometry('vertex') ||
26886                             (target.type !== 'way' && target.geometry(context.graph()) !== 'vertex');
26887                     }
26888                     return true;
26889                 }
26890
26891                 function updateHover(targets) {
26892
26893                     _selection.selectAll('.hover')
26894                         .classed('hover', false);
26895                     _selection.selectAll('.hover-suppressed')
26896                         .classed('hover-suppressed', false);
26897
26898                     var mode = context.mode();
26899
26900                     if (!_newNodeId && (mode.id === 'draw-line' || mode.id === 'draw-area')) {
26901                         var node = targets.find(function(target) {
26902                             return target instanceof osmEntity && target.type === 'node';
26903                         });
26904                         _newNodeId = node && node.id;
26905                     }
26906
26907                     targets = targets.filter(function(datum) {
26908                         if (datum instanceof osmEntity) {
26909                             // If drawing a way, don't hover on a node that was just placed. #3974
26910                             return datum.id !== _newNodeId &&
26911                                 (datum.type !== 'node' || !_ignoreVertex || allowsVertex(datum)) &&
26912                                 modeAllowsHover(datum);
26913                         }
26914                         return true;
26915                     });
26916
26917                     var selector = '';
26918
26919                     for (var i in targets) {
26920                         var datum = targets[i];
26921
26922                         // What are we hovering over?
26923                         if (datum.__featurehash__) {
26924                             // hovering custom data
26925                             selector += ', .data' + datum.__featurehash__;
26926
26927                         } else if (datum instanceof QAItem) {
26928                             selector += ', .' + datum.service + '.itemId-' + datum.id;
26929
26930                         } else if (datum instanceof osmNote) {
26931                             selector += ', .note-' + datum.id;
26932
26933                         } else if (datum instanceof osmEntity) {
26934                             selector += ', .' + datum.id;
26935                             if (datum.type === 'relation') {
26936                                 for (var j in datum.members) {
26937                                     selector += ', .' + datum.members[j].id;
26938                                 }
26939                             }
26940                         }
26941                     }
26942
26943                     var suppressed = _altDisables && event && event.altKey;
26944
26945                     if (selector.trim().length) {
26946                         // remove the first comma
26947                         selector = selector.slice(1);
26948                         _selection.selectAll(selector)
26949                             .classed(suppressed ? 'hover-suppressed' : 'hover', true);
26950                     }
26951
26952                     dispatch$1.call('hover', this, !suppressed && targets);
26953                 }
26954             }
26955
26956
26957             behavior.off = function(selection) {
26958                 selection.selectAll('.hover')
26959                     .classed('hover', false);
26960                 selection.selectAll('.hover-suppressed')
26961                     .classed('hover-suppressed', false);
26962                 selection
26963                     .classed('hover-disabled', false);
26964
26965                 selection
26966                     .on(_pointerPrefix + 'over.hover', null)
26967                     .on(_pointerPrefix + 'out.hover', null)
26968                     .on(_pointerPrefix + 'down.hover', null);
26969
26970                 select(window)
26971                     .on(_pointerPrefix + 'up.hover pointercancel.hover', null, true)
26972                     .on('keydown.hover', null)
26973                     .on('keyup.hover', null);
26974             };
26975
26976
26977             behavior.altDisables = function(val) {
26978                 if (!arguments.length) { return _altDisables; }
26979                 _altDisables = val;
26980                 return behavior;
26981             };
26982
26983             behavior.ignoreVertex = function(val) {
26984                 if (!arguments.length) { return _ignoreVertex; }
26985                 _ignoreVertex = val;
26986                 return behavior;
26987             };
26988
26989             behavior.initialNodeID = function(nodeId) {
26990                 _initialNodeID = nodeId;
26991                 return behavior;
26992             };
26993
26994             return utilRebind(behavior, dispatch$1, 'on');
26995         }
26996
26997         var _disableSpace = false;
26998         var _lastSpace = null;
26999
27000
27001         function behaviorDraw(context) {
27002             var dispatch$1 = dispatch(
27003                 'move', 'down', 'downcancel', 'click', 'clickWay', 'clickNode', 'undo', 'cancel', 'finish'
27004             );
27005
27006             var keybinding = utilKeybinding('draw');
27007
27008             var _hover = behaviorHover(context)
27009                 .altDisables(true)
27010                 .ignoreVertex(true)
27011                 .on('hover', context.ui().sidebar.hover);
27012             var _edit = behaviorEdit(context);
27013
27014             var _closeTolerance = 4;
27015             var _tolerance = 12;
27016             var _mouseLeave = false;
27017             var _lastMouse = null;
27018             var _lastPointerUpEvent;
27019
27020             var _downPointer;
27021
27022             // use pointer events on supported platforms; fallback to mouse events
27023             var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
27024
27025
27026             // related code
27027             // - `mode/drag_node.js` `datum()`
27028             function datum() {
27029                 var mode = context.mode();
27030                 var isNote = mode && (mode.id.indexOf('note') !== -1);
27031                 if (event.altKey || isNote) { return {}; }
27032
27033                 var element;
27034                 if (event.type === 'keydown') {
27035                     element = _lastMouse && _lastMouse.target;
27036                 } else {
27037                     element = event.target;
27038                 }
27039
27040                 // When drawing, snap only to touch targets..
27041                 // (this excludes area fills and active drawing elements)
27042                 var d = element.__data__;
27043                 return (d && d.properties && d.properties.target) ? d : {};
27044             }
27045
27046             function pointerdown() {
27047
27048                 if (_downPointer) { return; }
27049
27050                 var pointerLocGetter = utilFastMouse(this);
27051                 _downPointer = {
27052                     id: event.pointerId || 'mouse',
27053                     pointerLocGetter: pointerLocGetter,
27054                     downTime: +new Date(),
27055                     downLoc: pointerLocGetter(event)
27056                 };
27057
27058                 dispatch$1.call('down', this, datum());
27059             }
27060
27061             function pointerup() {
27062
27063                 if (!_downPointer || _downPointer.id !== (event.pointerId || 'mouse')) { return; }
27064
27065                 var downPointer = _downPointer;
27066                 _downPointer = null;
27067
27068                 _lastPointerUpEvent = event;
27069
27070                 if (downPointer.isCancelled) { return; }
27071
27072                 var t2 = +new Date();
27073                 var p2 = downPointer.pointerLocGetter(event);
27074                 var dist = geoVecLength(downPointer.downLoc, p2);
27075
27076                 if (dist < _closeTolerance || (dist < _tolerance && (t2 - downPointer.downTime) < 500)) {
27077                     // Prevent a quick second click
27078                     select(window).on('click.draw-block', function() {
27079                         event.stopPropagation();
27080                     }, true);
27081
27082                     context.map().dblclickZoomEnable(false);
27083
27084                     window.setTimeout(function() {
27085                         context.map().dblclickZoomEnable(true);
27086                         select(window).on('click.draw-block', null);
27087                     }, 500);
27088
27089                     click(p2);
27090                 }
27091             }
27092
27093             function pointermove() {
27094                 if (_downPointer &&
27095                     _downPointer.id === (event.pointerId || 'mouse') &&
27096                     !_downPointer.isCancelled) {
27097                     var p2 = _downPointer.pointerLocGetter(event);
27098                     var dist = geoVecLength(_downPointer.downLoc, p2);
27099                     if (dist >= _closeTolerance) {
27100                         _downPointer.isCancelled = true;
27101                         dispatch$1.call('downcancel', this);
27102                     }
27103                 }
27104
27105                 if ((event.pointerType && event.pointerType !== 'mouse') ||
27106                     event.buttons ||
27107                     _downPointer) { return; }
27108
27109                 // HACK: Mobile Safari likes to send one or more `mouse` type pointermove
27110                 // events immediately after non-mouse pointerup events; detect and ignore them.
27111                 if (_lastPointerUpEvent &&
27112                     _lastPointerUpEvent.pointerType !== 'mouse' &&
27113                     event.timeStamp - _lastPointerUpEvent.timeStamp < 100) { return; }
27114
27115                 _lastMouse = event;
27116                 dispatch$1.call('move', this, datum());
27117             }
27118
27119             function pointercancel() {
27120                 if (_downPointer &&
27121                     _downPointer.id === (event.pointerId || 'mouse')) {
27122
27123                     if (!_downPointer.isCancelled) {
27124                         dispatch$1.call('downcancel', this);
27125                     }
27126                     _downPointer = null;
27127                 }
27128             }
27129
27130             function mouseenter() {
27131                 _mouseLeave = false;
27132             }
27133
27134             function mouseleave() {
27135                 _mouseLeave = true;
27136             }
27137
27138             function allowsVertex(d) {
27139                 return d.geometry(context.graph()) === 'vertex' || _mainPresetIndex.allowsVertex(d, context.graph());
27140             }
27141
27142             // related code
27143             // - `mode/drag_node.js`     `doMove()`
27144             // - `behavior/draw.js`      `click()`
27145             // - `behavior/draw_way.js`  `move()`
27146             function click(loc) {
27147                 var d = datum();
27148                 var target = d && d.properties && d.properties.entity;
27149
27150                 var mode = context.mode();
27151
27152                 if (target && target.type === 'node' && allowsVertex(target)) {   // Snap to a node
27153                     dispatch$1.call('clickNode', this, target, d);
27154                     return;
27155
27156                 } else if (target && target.type === 'way' && (mode.id !== 'add-point' || mode.preset.matchGeometry('vertex'))) {   // Snap to a way
27157                     var choice = geoChooseEdge(
27158                         context.graph().childNodes(target), loc, context.projection, context.activeID()
27159                     );
27160                     if (choice) {
27161                         var edge = [target.nodes[choice.index - 1], target.nodes[choice.index]];
27162                         dispatch$1.call('clickWay', this, choice.loc, edge, d);
27163                         return;
27164                     }
27165                 } else if (mode.id !== 'add-point' || mode.preset.matchGeometry('point')) {
27166                     var locLatLng = context.projection.invert(loc);
27167                     dispatch$1.call('click', this, locLatLng, d);
27168                 }
27169
27170             }
27171
27172             // treat a spacebar press like a click
27173             function space() {
27174                 event.preventDefault();
27175                 event.stopPropagation();
27176
27177                 var currSpace = context.map().mouse();
27178                 if (_disableSpace && _lastSpace) {
27179                     var dist = geoVecLength(_lastSpace, currSpace);
27180                     if (dist > _tolerance) {
27181                         _disableSpace = false;
27182                     }
27183                 }
27184
27185                 if (_disableSpace || _mouseLeave || !_lastMouse) { return; }
27186
27187                 // user must move mouse or release space bar to allow another click
27188                 _lastSpace = currSpace;
27189                 _disableSpace = true;
27190
27191                 select(window).on('keyup.space-block', function() {
27192                     event.preventDefault();
27193                     event.stopPropagation();
27194                     _disableSpace = false;
27195                     select(window).on('keyup.space-block', null);
27196                 });
27197
27198                 // get the current mouse position
27199                 var loc = context.map().mouse() ||
27200                     // or the map center if the mouse has never entered the map
27201                     context.projection(context.map().center());
27202                 click(loc);
27203             }
27204
27205
27206             function backspace() {
27207                 event.preventDefault();
27208                 dispatch$1.call('undo');
27209             }
27210
27211
27212             function del() {
27213                 event.preventDefault();
27214                 dispatch$1.call('cancel');
27215             }
27216
27217
27218             function ret() {
27219                 event.preventDefault();
27220                 dispatch$1.call('finish');
27221             }
27222
27223
27224             function behavior(selection) {
27225                 context.install(_hover);
27226                 context.install(_edit);
27227
27228                 _downPointer = null;
27229
27230                 keybinding
27231                     .on('⌫', backspace)
27232                     .on('⌦', del)
27233                     .on('⎋', ret)
27234                     .on('↩', ret)
27235                     .on('space', space)
27236                     .on('⌥space', space);
27237
27238                 selection
27239                     .on('mouseenter.draw', mouseenter)
27240                     .on('mouseleave.draw', mouseleave)
27241                     .on(_pointerPrefix + 'down.draw', pointerdown)
27242                     .on(_pointerPrefix + 'move.draw', pointermove);
27243
27244                 select(window)
27245                     .on(_pointerPrefix + 'up.draw', pointerup, true)
27246                     .on('pointercancel.draw', pointercancel, true);
27247
27248                 select(document)
27249                     .call(keybinding);
27250
27251                 return behavior;
27252             }
27253
27254
27255             behavior.off = function(selection) {
27256                 context.ui().sidebar.hover.cancel();
27257                 context.uninstall(_hover);
27258                 context.uninstall(_edit);
27259
27260                 selection
27261                     .on('mouseenter.draw', null)
27262                     .on('mouseleave.draw', null)
27263                     .on(_pointerPrefix + 'down.draw', null)
27264                     .on(_pointerPrefix + 'move.draw', null);
27265
27266                 select(window)
27267                     .on(_pointerPrefix + 'up.draw', null)
27268                     .on('pointercancel.draw', null);
27269                     // note: keyup.space-block, click.draw-block should remain
27270
27271                 select(document)
27272                     .call(keybinding.unbind);
27273             };
27274
27275
27276             behavior.hover = function() {
27277                 return _hover;
27278             };
27279
27280
27281             return utilRebind(behavior, dispatch$1, 'on');
27282         }
27283
27284         function initRange(domain, range) {
27285           switch (arguments.length) {
27286             case 0: break;
27287             case 1: this.range(domain); break;
27288             default: this.range(range).domain(domain); break;
27289           }
27290           return this;
27291         }
27292
27293         var prefix = "$";
27294
27295         function Map$1() {}
27296
27297         Map$1.prototype = map$2.prototype = {
27298           constructor: Map$1,
27299           has: function(key) {
27300             return (prefix + key) in this;
27301           },
27302           get: function(key) {
27303             return this[prefix + key];
27304           },
27305           set: function(key, value) {
27306             this[prefix + key] = value;
27307             return this;
27308           },
27309           remove: function(key) {
27310             var property = prefix + key;
27311             return property in this && delete this[property];
27312           },
27313           clear: function() {
27314             for (var property in this) { if (property[0] === prefix) { delete this[property]; } }
27315           },
27316           keys: function() {
27317             var keys = [];
27318             for (var property in this) { if (property[0] === prefix) { keys.push(property.slice(1)); } }
27319             return keys;
27320           },
27321           values: function() {
27322             var values = [];
27323             for (var property in this) { if (property[0] === prefix) { values.push(this[property]); } }
27324             return values;
27325           },
27326           entries: function() {
27327             var entries = [];
27328             for (var property in this) { if (property[0] === prefix) { entries.push({key: property.slice(1), value: this[property]}); } }
27329             return entries;
27330           },
27331           size: function() {
27332             var size = 0;
27333             for (var property in this) { if (property[0] === prefix) { ++size; } }
27334             return size;
27335           },
27336           empty: function() {
27337             for (var property in this) { if (property[0] === prefix) { return false; } }
27338             return true;
27339           },
27340           each: function(f) {
27341             for (var property in this) { if (property[0] === prefix) { f(this[property], property.slice(1), this); } }
27342           }
27343         };
27344
27345         function map$2(object, f) {
27346           var map = new Map$1;
27347
27348           // Copy constructor.
27349           if (object instanceof Map$1) { object.each(function(value, key) { map.set(key, value); }); }
27350
27351           // Index array by numeric index or specified key function.
27352           else if (Array.isArray(object)) {
27353             var i = -1,
27354                 n = object.length,
27355                 o;
27356
27357             if (f == null) { while (++i < n) { map.set(i, object[i]); } }
27358             else { while (++i < n) { map.set(f(o = object[i], i, object), o); } }
27359           }
27360
27361           // Convert object to map.
27362           else if (object) { for (var key in object) { map.set(key, object[key]); } }
27363
27364           return map;
27365         }
27366
27367         function Set$1() {}
27368
27369         var proto = map$2.prototype;
27370
27371         Set$1.prototype = set$2.prototype = {
27372           constructor: Set$1,
27373           has: proto.has,
27374           add: function(value) {
27375             value += "";
27376             this[prefix + value] = value;
27377             return this;
27378           },
27379           remove: proto.remove,
27380           clear: proto.clear,
27381           values: proto.keys,
27382           size: proto.size,
27383           empty: proto.empty,
27384           each: proto.each
27385         };
27386
27387         function set$2(object, f) {
27388           var set = new Set$1;
27389
27390           // Copy constructor.
27391           if (object instanceof Set$1) { object.each(function(value) { set.add(value); }); }
27392
27393           // Otherwise, assume it’s an array.
27394           else if (object) {
27395             var i = -1, n = object.length;
27396             if (f == null) { while (++i < n) { set.add(object[i]); } }
27397             else { while (++i < n) { set.add(f(object[i], i, object)); } }
27398           }
27399
27400           return set;
27401         }
27402
27403         var array$1 = Array.prototype;
27404
27405         var map$3 = array$1.map;
27406         var slice$4 = array$1.slice;
27407
27408         function constant$4(x) {
27409           return function() {
27410             return x;
27411           };
27412         }
27413
27414         function number$1(x) {
27415           return +x;
27416         }
27417
27418         var unit = [0, 1];
27419
27420         function identity$3(x) {
27421           return x;
27422         }
27423
27424         function normalize(a, b) {
27425           return (b -= (a = +a))
27426               ? function(x) { return (x - a) / b; }
27427               : constant$4(isNaN(b) ? NaN : 0.5);
27428         }
27429
27430         function clamper(domain) {
27431           var a = domain[0], b = domain[domain.length - 1], t;
27432           if (a > b) { t = a, a = b, b = t; }
27433           return function(x) { return Math.max(a, Math.min(b, x)); };
27434         }
27435
27436         // normalize(a, b)(x) takes a domain value x in [a,b] and returns the corresponding parameter t in [0,1].
27437         // interpolate(a, b)(t) takes a parameter t in [0,1] and returns the corresponding range value x in [a,b].
27438         function bimap(domain, range, interpolate) {
27439           var d0 = domain[0], d1 = domain[1], r0 = range[0], r1 = range[1];
27440           if (d1 < d0) { d0 = normalize(d1, d0), r0 = interpolate(r1, r0); }
27441           else { d0 = normalize(d0, d1), r0 = interpolate(r0, r1); }
27442           return function(x) { return r0(d0(x)); };
27443         }
27444
27445         function polymap(domain, range, interpolate) {
27446           var j = Math.min(domain.length, range.length) - 1,
27447               d = new Array(j),
27448               r = new Array(j),
27449               i = -1;
27450
27451           // Reverse descending domains.
27452           if (domain[j] < domain[0]) {
27453             domain = domain.slice().reverse();
27454             range = range.slice().reverse();
27455           }
27456
27457           while (++i < j) {
27458             d[i] = normalize(domain[i], domain[i + 1]);
27459             r[i] = interpolate(range[i], range[i + 1]);
27460           }
27461
27462           return function(x) {
27463             var i = bisectRight(domain, x, 1, j) - 1;
27464             return r[i](d[i](x));
27465           };
27466         }
27467
27468         function copy$1(source, target) {
27469           return target
27470               .domain(source.domain())
27471               .range(source.range())
27472               .interpolate(source.interpolate())
27473               .clamp(source.clamp())
27474               .unknown(source.unknown());
27475         }
27476
27477         function transformer$1() {
27478           var domain = unit,
27479               range = unit,
27480               interpolate$1 = interpolate,
27481               transform,
27482               untransform,
27483               unknown,
27484               clamp = identity$3,
27485               piecewise,
27486               output,
27487               input;
27488
27489           function rescale() {
27490             piecewise = Math.min(domain.length, range.length) > 2 ? polymap : bimap;
27491             output = input = null;
27492             return scale;
27493           }
27494
27495           function scale(x) {
27496             return isNaN(x = +x) ? unknown : (output || (output = piecewise(domain.map(transform), range, interpolate$1)))(transform(clamp(x)));
27497           }
27498
27499           scale.invert = function(y) {
27500             return clamp(untransform((input || (input = piecewise(range, domain.map(transform), d3_interpolateNumber)))(y)));
27501           };
27502
27503           scale.domain = function(_) {
27504             return arguments.length ? (domain = map$3.call(_, number$1), clamp === identity$3 || (clamp = clamper(domain)), rescale()) : domain.slice();
27505           };
27506
27507           scale.range = function(_) {
27508             return arguments.length ? (range = slice$4.call(_), rescale()) : range.slice();
27509           };
27510
27511           scale.rangeRound = function(_) {
27512             return range = slice$4.call(_), interpolate$1 = interpolateRound, rescale();
27513           };
27514
27515           scale.clamp = function(_) {
27516             return arguments.length ? (clamp = _ ? clamper(domain) : identity$3, scale) : clamp !== identity$3;
27517           };
27518
27519           scale.interpolate = function(_) {
27520             return arguments.length ? (interpolate$1 = _, rescale()) : interpolate$1;
27521           };
27522
27523           scale.unknown = function(_) {
27524             return arguments.length ? (unknown = _, scale) : unknown;
27525           };
27526
27527           return function(t, u) {
27528             transform = t, untransform = u;
27529             return rescale();
27530           };
27531         }
27532
27533         function continuous(transform, untransform) {
27534           return transformer$1()(transform, untransform);
27535         }
27536
27537         // Computes the decimal coefficient and exponent of the specified number x with
27538         // significant digits p, where x is positive and p is in [1, 21] or undefined.
27539         // For example, formatDecimal(1.23) returns ["123", 0].
27540         function formatDecimal(x, p) {
27541           if ((i = (x = p ? x.toExponential(p - 1) : x.toExponential()).indexOf("e")) < 0) { return null; } // NaN, ±Infinity
27542           var i, coefficient = x.slice(0, i);
27543
27544           // The string returned by toExponential either has the form \d\.\d+e[-+]\d+
27545           // (e.g., 1.2e+3) or the form \de[-+]\d+ (e.g., 1e+3).
27546           return [
27547             coefficient.length > 1 ? coefficient[0] + coefficient.slice(2) : coefficient,
27548             +x.slice(i + 1)
27549           ];
27550         }
27551
27552         function exponent(x) {
27553           return x = formatDecimal(Math.abs(x)), x ? x[1] : NaN;
27554         }
27555
27556         function formatGroup(grouping, thousands) {
27557           return function(value, width) {
27558             var i = value.length,
27559                 t = [],
27560                 j = 0,
27561                 g = grouping[0],
27562                 length = 0;
27563
27564             while (i > 0 && g > 0) {
27565               if (length + g + 1 > width) { g = Math.max(1, width - length); }
27566               t.push(value.substring(i -= g, i + g));
27567               if ((length += g + 1) > width) { break; }
27568               g = grouping[j = (j + 1) % grouping.length];
27569             }
27570
27571             return t.reverse().join(thousands);
27572           };
27573         }
27574
27575         function formatNumerals(numerals) {
27576           return function(value) {
27577             return value.replace(/[0-9]/g, function(i) {
27578               return numerals[+i];
27579             });
27580           };
27581         }
27582
27583         // [[fill]align][sign][symbol][0][width][,][.precision][~][type]
27584         var re = /^(?:(.)?([<>=^]))?([+\-( ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?(~)?([a-z%])?$/i;
27585
27586         function formatSpecifier(specifier) {
27587           if (!(match = re.exec(specifier))) { throw new Error("invalid format: " + specifier); }
27588           var match;
27589           return new FormatSpecifier({
27590             fill: match[1],
27591             align: match[2],
27592             sign: match[3],
27593             symbol: match[4],
27594             zero: match[5],
27595             width: match[6],
27596             comma: match[7],
27597             precision: match[8] && match[8].slice(1),
27598             trim: match[9],
27599             type: match[10]
27600           });
27601         }
27602
27603         formatSpecifier.prototype = FormatSpecifier.prototype; // instanceof
27604
27605         function FormatSpecifier(specifier) {
27606           this.fill = specifier.fill === undefined ? " " : specifier.fill + "";
27607           this.align = specifier.align === undefined ? ">" : specifier.align + "";
27608           this.sign = specifier.sign === undefined ? "-" : specifier.sign + "";
27609           this.symbol = specifier.symbol === undefined ? "" : specifier.symbol + "";
27610           this.zero = !!specifier.zero;
27611           this.width = specifier.width === undefined ? undefined : +specifier.width;
27612           this.comma = !!specifier.comma;
27613           this.precision = specifier.precision === undefined ? undefined : +specifier.precision;
27614           this.trim = !!specifier.trim;
27615           this.type = specifier.type === undefined ? "" : specifier.type + "";
27616         }
27617
27618         FormatSpecifier.prototype.toString = function() {
27619           return this.fill
27620               + this.align
27621               + this.sign
27622               + this.symbol
27623               + (this.zero ? "0" : "")
27624               + (this.width === undefined ? "" : Math.max(1, this.width | 0))
27625               + (this.comma ? "," : "")
27626               + (this.precision === undefined ? "" : "." + Math.max(0, this.precision | 0))
27627               + (this.trim ? "~" : "")
27628               + this.type;
27629         };
27630
27631         // Trims insignificant zeros, e.g., replaces 1.2000k with 1.2k.
27632         function formatTrim(s) {
27633           out: for (var n = s.length, i = 1, i0 = -1, i1; i < n; ++i) {
27634             switch (s[i]) {
27635               case ".": i0 = i1 = i; break;
27636               case "0": if (i0 === 0) { i0 = i; } i1 = i; break;
27637               default: if (!+s[i]) { break out; } if (i0 > 0) { i0 = 0; } break;
27638             }
27639           }
27640           return i0 > 0 ? s.slice(0, i0) + s.slice(i1 + 1) : s;
27641         }
27642
27643         var prefixExponent;
27644
27645         function formatPrefixAuto(x, p) {
27646           var d = formatDecimal(x, p);
27647           if (!d) { return x + ""; }
27648           var coefficient = d[0],
27649               exponent = d[1],
27650               i = exponent - (prefixExponent = Math.max(-8, Math.min(8, Math.floor(exponent / 3))) * 3) + 1,
27651               n = coefficient.length;
27652           return i === n ? coefficient
27653               : i > n ? coefficient + new Array(i - n + 1).join("0")
27654               : i > 0 ? coefficient.slice(0, i) + "." + coefficient.slice(i)
27655               : "0." + new Array(1 - i).join("0") + formatDecimal(x, Math.max(0, p + i - 1))[0]; // less than 1y!
27656         }
27657
27658         function formatRounded(x, p) {
27659           var d = formatDecimal(x, p);
27660           if (!d) { return x + ""; }
27661           var coefficient = d[0],
27662               exponent = d[1];
27663           return exponent < 0 ? "0." + new Array(-exponent).join("0") + coefficient
27664               : coefficient.length > exponent + 1 ? coefficient.slice(0, exponent + 1) + "." + coefficient.slice(exponent + 1)
27665               : coefficient + new Array(exponent - coefficient.length + 2).join("0");
27666         }
27667
27668         var formatTypes = {
27669           "%": function(x, p) { return (x * 100).toFixed(p); },
27670           "b": function(x) { return Math.round(x).toString(2); },
27671           "c": function(x) { return x + ""; },
27672           "d": function(x) { return Math.round(x).toString(10); },
27673           "e": function(x, p) { return x.toExponential(p); },
27674           "f": function(x, p) { return x.toFixed(p); },
27675           "g": function(x, p) { return x.toPrecision(p); },
27676           "o": function(x) { return Math.round(x).toString(8); },
27677           "p": function(x, p) { return formatRounded(x * 100, p); },
27678           "r": formatRounded,
27679           "s": formatPrefixAuto,
27680           "X": function(x) { return Math.round(x).toString(16).toUpperCase(); },
27681           "x": function(x) { return Math.round(x).toString(16); }
27682         };
27683
27684         function identity$4(x) {
27685           return x;
27686         }
27687
27688         var map$4 = Array.prototype.map,
27689             prefixes = ["y","z","a","f","p","n","µ","m","","k","M","G","T","P","E","Z","Y"];
27690
27691         function formatLocale(locale) {
27692           var group = locale.grouping === undefined || locale.thousands === undefined ? identity$4 : formatGroup(map$4.call(locale.grouping, Number), locale.thousands + ""),
27693               currencyPrefix = locale.currency === undefined ? "" : locale.currency[0] + "",
27694               currencySuffix = locale.currency === undefined ? "" : locale.currency[1] + "",
27695               decimal = locale.decimal === undefined ? "." : locale.decimal + "",
27696               numerals = locale.numerals === undefined ? identity$4 : formatNumerals(map$4.call(locale.numerals, String)),
27697               percent = locale.percent === undefined ? "%" : locale.percent + "",
27698               minus = locale.minus === undefined ? "-" : locale.minus + "",
27699               nan = locale.nan === undefined ? "NaN" : locale.nan + "";
27700
27701           function newFormat(specifier) {
27702             specifier = formatSpecifier(specifier);
27703
27704             var fill = specifier.fill,
27705                 align = specifier.align,
27706                 sign = specifier.sign,
27707                 symbol = specifier.symbol,
27708                 zero = specifier.zero,
27709                 width = specifier.width,
27710                 comma = specifier.comma,
27711                 precision = specifier.precision,
27712                 trim = specifier.trim,
27713                 type = specifier.type;
27714
27715             // The "n" type is an alias for ",g".
27716             if (type === "n") { comma = true, type = "g"; }
27717
27718             // The "" type, and any invalid type, is an alias for ".12~g".
27719             else if (!formatTypes[type]) { precision === undefined && (precision = 12), trim = true, type = "g"; }
27720
27721             // If zero fill is specified, padding goes after sign and before digits.
27722             if (zero || (fill === "0" && align === "=")) { zero = true, fill = "0", align = "="; }
27723
27724             // Compute the prefix and suffix.
27725             // For SI-prefix, the suffix is lazily computed.
27726             var prefix = symbol === "$" ? currencyPrefix : symbol === "#" && /[boxX]/.test(type) ? "0" + type.toLowerCase() : "",
27727                 suffix = symbol === "$" ? currencySuffix : /[%p]/.test(type) ? percent : "";
27728
27729             // What format function should we use?
27730             // Is this an integer type?
27731             // Can this type generate exponential notation?
27732             var formatType = formatTypes[type],
27733                 maybeSuffix = /[defgprs%]/.test(type);
27734
27735             // Set the default precision if not specified,
27736             // or clamp the specified precision to the supported range.
27737             // For significant precision, it must be in [1, 21].
27738             // For fixed precision, it must be in [0, 20].
27739             precision = precision === undefined ? 6
27740                 : /[gprs]/.test(type) ? Math.max(1, Math.min(21, precision))
27741                 : Math.max(0, Math.min(20, precision));
27742
27743             function format(value) {
27744               var valuePrefix = prefix,
27745                   valueSuffix = suffix,
27746                   i, n, c;
27747
27748               if (type === "c") {
27749                 valueSuffix = formatType(value) + valueSuffix;
27750                 value = "";
27751               } else {
27752                 value = +value;
27753
27754                 // Determine the sign. -0 is not less than 0, but 1 / -0 is!
27755                 var valueNegative = value < 0 || 1 / value < 0;
27756
27757                 // Perform the initial formatting.
27758                 value = isNaN(value) ? nan : formatType(Math.abs(value), precision);
27759
27760                 // Trim insignificant zeros.
27761                 if (trim) { value = formatTrim(value); }
27762
27763                 // If a negative value rounds to zero after formatting, and no explicit positive sign is requested, hide the sign.
27764                 if (valueNegative && +value === 0 && sign !== "+") { valueNegative = false; }
27765
27766                 // Compute the prefix and suffix.
27767                 valuePrefix = (valueNegative ? (sign === "(" ? sign : minus) : sign === "-" || sign === "(" ? "" : sign) + valuePrefix;
27768                 valueSuffix = (type === "s" ? prefixes[8 + prefixExponent / 3] : "") + valueSuffix + (valueNegative && sign === "(" ? ")" : "");
27769
27770                 // Break the formatted value into the integer “value” part that can be
27771                 // grouped, and fractional or exponential “suffix” part that is not.
27772                 if (maybeSuffix) {
27773                   i = -1, n = value.length;
27774                   while (++i < n) {
27775                     if (c = value.charCodeAt(i), 48 > c || c > 57) {
27776                       valueSuffix = (c === 46 ? decimal + value.slice(i + 1) : value.slice(i)) + valueSuffix;
27777                       value = value.slice(0, i);
27778                       break;
27779                     }
27780                   }
27781                 }
27782               }
27783
27784               // If the fill character is not "0", grouping is applied before padding.
27785               if (comma && !zero) { value = group(value, Infinity); }
27786
27787               // Compute the padding.
27788               var length = valuePrefix.length + value.length + valueSuffix.length,
27789                   padding = length < width ? new Array(width - length + 1).join(fill) : "";
27790
27791               // If the fill character is "0", grouping is applied after padding.
27792               if (comma && zero) { value = group(padding + value, padding.length ? width - valueSuffix.length : Infinity), padding = ""; }
27793
27794               // Reconstruct the final output based on the desired alignment.
27795               switch (align) {
27796                 case "<": value = valuePrefix + value + valueSuffix + padding; break;
27797                 case "=": value = valuePrefix + padding + value + valueSuffix; break;
27798                 case "^": value = padding.slice(0, length = padding.length >> 1) + valuePrefix + value + valueSuffix + padding.slice(length); break;
27799                 default: value = padding + valuePrefix + value + valueSuffix; break;
27800               }
27801
27802               return numerals(value);
27803             }
27804
27805             format.toString = function() {
27806               return specifier + "";
27807             };
27808
27809             return format;
27810           }
27811
27812           function formatPrefix(specifier, value) {
27813             var f = newFormat((specifier = formatSpecifier(specifier), specifier.type = "f", specifier)),
27814                 e = Math.max(-8, Math.min(8, Math.floor(exponent(value) / 3))) * 3,
27815                 k = Math.pow(10, -e),
27816                 prefix = prefixes[8 + e / 3];
27817             return function(value) {
27818               return f(k * value) + prefix;
27819             };
27820           }
27821
27822           return {
27823             format: newFormat,
27824             formatPrefix: formatPrefix
27825           };
27826         }
27827
27828         var locale;
27829         var format;
27830         var formatPrefix;
27831
27832         defaultLocale({
27833           decimal: ".",
27834           thousands: ",",
27835           grouping: [3],
27836           currency: ["$", ""],
27837           minus: "-"
27838         });
27839
27840         function defaultLocale(definition) {
27841           locale = formatLocale(definition);
27842           format = locale.format;
27843           formatPrefix = locale.formatPrefix;
27844           return locale;
27845         }
27846
27847         function precisionFixed(step) {
27848           return Math.max(0, -exponent(Math.abs(step)));
27849         }
27850
27851         function precisionPrefix(step, value) {
27852           return Math.max(0, Math.max(-8, Math.min(8, Math.floor(exponent(value) / 3))) * 3 - exponent(Math.abs(step)));
27853         }
27854
27855         function precisionRound(step, max) {
27856           step = Math.abs(step), max = Math.abs(max) - step;
27857           return Math.max(0, exponent(max) - exponent(step)) + 1;
27858         }
27859
27860         function tickFormat(start, stop, count, specifier) {
27861           var step = tickStep(start, stop, count),
27862               precision;
27863           specifier = formatSpecifier(specifier == null ? ",f" : specifier);
27864           switch (specifier.type) {
27865             case "s": {
27866               var value = Math.max(Math.abs(start), Math.abs(stop));
27867               if (specifier.precision == null && !isNaN(precision = precisionPrefix(step, value))) { specifier.precision = precision; }
27868               return formatPrefix(specifier, value);
27869             }
27870             case "":
27871             case "e":
27872             case "g":
27873             case "p":
27874             case "r": {
27875               if (specifier.precision == null && !isNaN(precision = precisionRound(step, Math.max(Math.abs(start), Math.abs(stop))))) { specifier.precision = precision - (specifier.type === "e"); }
27876               break;
27877             }
27878             case "f":
27879             case "%": {
27880               if (specifier.precision == null && !isNaN(precision = precisionFixed(step))) { specifier.precision = precision - (specifier.type === "%") * 2; }
27881               break;
27882             }
27883           }
27884           return format(specifier);
27885         }
27886
27887         function linearish(scale) {
27888           var domain = scale.domain;
27889
27890           scale.ticks = function(count) {
27891             var d = domain();
27892             return ticks(d[0], d[d.length - 1], count == null ? 10 : count);
27893           };
27894
27895           scale.tickFormat = function(count, specifier) {
27896             var d = domain();
27897             return tickFormat(d[0], d[d.length - 1], count == null ? 10 : count, specifier);
27898           };
27899
27900           scale.nice = function(count) {
27901             if (count == null) { count = 10; }
27902
27903             var d = domain(),
27904                 i0 = 0,
27905                 i1 = d.length - 1,
27906                 start = d[i0],
27907                 stop = d[i1],
27908                 step;
27909
27910             if (stop < start) {
27911               step = start, start = stop, stop = step;
27912               step = i0, i0 = i1, i1 = step;
27913             }
27914
27915             step = tickIncrement(start, stop, count);
27916
27917             if (step > 0) {
27918               start = Math.floor(start / step) * step;
27919               stop = Math.ceil(stop / step) * step;
27920               step = tickIncrement(start, stop, count);
27921             } else if (step < 0) {
27922               start = Math.ceil(start * step) / step;
27923               stop = Math.floor(stop * step) / step;
27924               step = tickIncrement(start, stop, count);
27925             }
27926
27927             if (step > 0) {
27928               d[i0] = Math.floor(start / step) * step;
27929               d[i1] = Math.ceil(stop / step) * step;
27930               domain(d);
27931             } else if (step < 0) {
27932               d[i0] = Math.ceil(start * step) / step;
27933               d[i1] = Math.floor(stop * step) / step;
27934               domain(d);
27935             }
27936
27937             return scale;
27938           };
27939
27940           return scale;
27941         }
27942
27943         function linear$2() {
27944           var scale = continuous(identity$3, identity$3);
27945
27946           scale.copy = function() {
27947             return copy$1(scale, linear$2());
27948           };
27949
27950           initRange.apply(scale, arguments);
27951
27952           return linearish(scale);
27953         }
27954
27955         function quantize() {
27956           var x0 = 0,
27957               x1 = 1,
27958               n = 1,
27959               domain = [0.5],
27960               range = [0, 1],
27961               unknown;
27962
27963           function scale(x) {
27964             return x <= x ? range[bisectRight(domain, x, 0, n)] : unknown;
27965           }
27966
27967           function rescale() {
27968             var i = -1;
27969             domain = new Array(n);
27970             while (++i < n) { domain[i] = ((i + 1) * x1 - (i - n) * x0) / (n + 1); }
27971             return scale;
27972           }
27973
27974           scale.domain = function(_) {
27975             return arguments.length ? (x0 = +_[0], x1 = +_[1], rescale()) : [x0, x1];
27976           };
27977
27978           scale.range = function(_) {
27979             return arguments.length ? (n = (range = slice$4.call(_)).length - 1, rescale()) : range.slice();
27980           };
27981
27982           scale.invertExtent = function(y) {
27983             var i = range.indexOf(y);
27984             return i < 0 ? [NaN, NaN]
27985                 : i < 1 ? [x0, domain[0]]
27986                 : i >= n ? [domain[n - 1], x1]
27987                 : [domain[i - 1], domain[i]];
27988           };
27989
27990           scale.unknown = function(_) {
27991             return arguments.length ? (unknown = _, scale) : scale;
27992           };
27993
27994           scale.thresholds = function() {
27995             return domain.slice();
27996           };
27997
27998           scale.copy = function() {
27999             return quantize()
28000                 .domain([x0, x1])
28001                 .range(range)
28002                 .unknown(unknown);
28003           };
28004
28005           return initRange.apply(linearish(scale), arguments);
28006         }
28007
28008         function behaviorBreathe() {
28009             var duration = 800;
28010             var steps = 4;
28011             var selector = '.selected.shadow, .selected .shadow';
28012             var _selected = select(null);
28013             var _classed = '';
28014             var _params = {};
28015             var _done = false;
28016             var _timer;
28017
28018
28019             function ratchetyInterpolator(a, b, steps, units) {
28020                 a = parseFloat(a);
28021                 b = parseFloat(b);
28022                 var sample = quantize()
28023                     .domain([0, 1])
28024                     .range(d3_quantize(d3_interpolateNumber(a, b), steps));
28025
28026                 return function(t) {
28027                     return String(sample(t)) + (units || '');
28028                 };
28029             }
28030
28031
28032             function reset(selection) {
28033                 selection
28034                     .style('stroke-opacity', null)
28035                     .style('stroke-width', null)
28036                     .style('fill-opacity', null)
28037                     .style('r', null);
28038             }
28039
28040
28041             function setAnimationParams(transition, fromTo) {
28042                 var toFrom = (fromTo === 'from' ? 'to' : 'from');
28043
28044                 transition
28045                     .styleTween('stroke-opacity', function(d) {
28046                         return ratchetyInterpolator(
28047                             _params[d.id][toFrom].opacity,
28048                             _params[d.id][fromTo].opacity,
28049                             steps
28050                         );
28051                     })
28052                     .styleTween('stroke-width', function(d) {
28053                         return ratchetyInterpolator(
28054                             _params[d.id][toFrom].width,
28055                             _params[d.id][fromTo].width,
28056                             steps,
28057                             'px'
28058                         );
28059                     })
28060                     .styleTween('fill-opacity', function(d) {
28061                         return ratchetyInterpolator(
28062                             _params[d.id][toFrom].opacity,
28063                             _params[d.id][fromTo].opacity,
28064                             steps
28065                         );
28066                     })
28067                     .styleTween('r', function(d) {
28068                         return ratchetyInterpolator(
28069                             _params[d.id][toFrom].width,
28070                             _params[d.id][fromTo].width,
28071                             steps,
28072                             'px'
28073                         );
28074                     });
28075             }
28076
28077
28078             function calcAnimationParams(selection) {
28079                 selection
28080                     .call(reset)
28081                     .each(function(d) {
28082                         var s = select(this);
28083                         var tag = s.node().tagName;
28084                         var p = {'from': {}, 'to': {}};
28085                         var opacity;
28086                         var width;
28087
28088                         // determine base opacity and width
28089                         if (tag === 'circle') {
28090                             opacity = parseFloat(s.style('fill-opacity') || 0.5);
28091                             width = parseFloat(s.style('r') || 15.5);
28092                         } else {
28093                             opacity = parseFloat(s.style('stroke-opacity') || 0.7);
28094                             width = parseFloat(s.style('stroke-width') || 10);
28095                         }
28096
28097                         // calculate from/to interpolation params..
28098                         p.tag = tag;
28099                         p.from.opacity = opacity * 0.6;
28100                         p.to.opacity = opacity * 1.25;
28101                         p.from.width = width * 0.7;
28102                         p.to.width = width * (tag === 'circle' ? 1.5 : 1);
28103                         _params[d.id] = p;
28104                     });
28105             }
28106
28107
28108             function run(surface, fromTo) {
28109                 var toFrom = (fromTo === 'from' ? 'to' : 'from');
28110                 var currSelected = surface.selectAll(selector);
28111                 var currClassed = surface.attr('class');
28112
28113                 if (_done || currSelected.empty()) {
28114                     _selected.call(reset);
28115                     _selected = select(null);
28116                     return;
28117                 }
28118
28119                 if (!fastDeepEqual(currSelected.data(), _selected.data()) || currClassed !== _classed) {
28120                     _selected.call(reset);
28121                     _classed = currClassed;
28122                     _selected = currSelected.call(calcAnimationParams);
28123                 }
28124
28125                 var didCallNextRun = false;
28126
28127                 _selected
28128                     .transition()
28129                     .duration(duration)
28130                     .call(setAnimationParams, fromTo)
28131                     .on('end', function() {
28132                         // `end` event is called for each selected element, but we want
28133                         // it to run only once
28134                         if (!didCallNextRun) {
28135                             surface.call(run, toFrom);
28136                             didCallNextRun = true;
28137                         }
28138
28139                         // if entity was deselected, remove breathe styling
28140                         if (!select(this).classed('selected')) {
28141                             reset(select(this));
28142                         }
28143                     });
28144             }
28145
28146             function behavior(surface) {
28147                 _done = false;
28148                 _timer = timer(function() {
28149                     // wait for elements to actually become selected
28150                     if (surface.selectAll(selector).empty()) {
28151                         return false;
28152                     }
28153
28154                     surface.call(run, 'from');
28155                     _timer.stop();
28156                     return true;
28157                 }, 20);
28158             }
28159
28160             behavior.restartIfNeeded = function(surface) {
28161                 if (_selected.empty()) {
28162                     surface.call(run, 'from');
28163                     if (_timer) {
28164                         _timer.stop();
28165                     }
28166                 }
28167             };
28168
28169             behavior.off = function() {
28170                 _done = true;
28171                 if (_timer) {
28172                     _timer.stop();
28173                 }
28174                 _selected
28175                     .interrupt()
28176                     .call(reset);
28177             };
28178
28179
28180             return behavior;
28181         }
28182
28183         /* Creates a keybinding behavior for an operation */
28184         function behaviorOperation(context) {
28185             var _operation;
28186
28187             function keypress() {
28188                 // prevent operations during low zoom selection
28189                 if (!context.map().withinEditableZoom()) { return; }
28190
28191                 event.preventDefault();
28192                 var disabled = _operation.disabled();
28193
28194                 if (disabled) {
28195                     context.ui().flash
28196                         .duration(4000)
28197                         .iconName('#iD-operation-' + _operation.id)
28198                         .iconClass('operation disabled')
28199                         .text(_operation.tooltip)();
28200
28201                 } else {
28202                     context.ui().flash
28203                         .duration(2000)
28204                         .iconName('#iD-operation-' + _operation.id)
28205                         .iconClass('operation')
28206                         .text(_operation.annotation() || _operation.title)();
28207
28208                     if (_operation.point) { _operation.point(null); }
28209                     _operation();
28210                 }
28211             }
28212
28213
28214             function behavior() {
28215                 if (_operation && _operation.available()) {
28216                     context.keybinding()
28217                         .on(_operation.keys, keypress);
28218                 }
28219
28220                 return behavior;
28221             }
28222
28223
28224             behavior.off = function() {
28225                 context.keybinding()
28226                     .off(_operation.keys);
28227             };
28228
28229
28230             behavior.which = function (_) {
28231                 if (!arguments.length) { return _operation; }
28232                 _operation = _;
28233                 return behavior;
28234             };
28235
28236
28237             return behavior;
28238         }
28239
28240         function operationCircularize(context, selectedIDs) {
28241             var _extent;
28242             var _actions = selectedIDs.map(getAction).filter(Boolean);
28243             var _amount = _actions.length === 1 ? 'single' : 'multiple';
28244             var _coords = utilGetAllNodes(selectedIDs, context.graph())
28245                 .map(function(n) { return n.loc; });
28246
28247             function getAction(entityID) {
28248
28249                 var entity = context.entity(entityID);
28250
28251                 if (entity.type !== 'way' || new Set(entity.nodes).size <= 1) { return null; }
28252
28253                 if (!_extent) {
28254                     _extent =  entity.extent(context.graph());
28255                 } else {
28256                     _extent = _extent.extend(entity.extent(context.graph()));
28257                 }
28258
28259                 return actionCircularize(entityID, context.projection);
28260             }
28261
28262             var operation = function() {
28263                 if (!_actions.length) { return; }
28264
28265                 var combinedAction = function(graph, t) {
28266                     _actions.forEach(function(action) {
28267                         if (!action.disabled(graph)) {
28268                             graph = action(graph, t);
28269                         }
28270                     });
28271                     return graph;
28272                 };
28273                 combinedAction.transitionable = true;
28274
28275                 context.perform(combinedAction, operation.annotation());
28276
28277                 window.setTimeout(function() {
28278                     context.validator().validate();
28279                 }, 300);  // after any transition
28280             };
28281
28282
28283             operation.available = function() {
28284                 return _actions.length && selectedIDs.length === _actions.length;
28285             };
28286
28287
28288             // don't cache this because the visible extent could change
28289             operation.disabled = function() {
28290                 if (!_actions.length) { return ''; }
28291
28292                 var actionDisableds = _actions.map(function(action) {
28293                     return action.disabled(context.graph());
28294                 }).filter(Boolean);
28295
28296                 if (actionDisableds.length === _actions.length) {
28297                     // none of the features can be circularized
28298
28299                     if (new Set(actionDisableds).size > 1) {
28300                         return 'multiple_blockers';
28301                     }
28302                     return actionDisableds[0];
28303                 } else if (_extent.percentContainedIn(context.map().extent()) < 0.8) {
28304                     return 'too_large';
28305                 } else if (someMissing()) {
28306                     return 'not_downloaded';
28307                 } else if (selectedIDs.some(context.hasHiddenConnections)) {
28308                     return 'connected_to_hidden';
28309                 }
28310
28311                 return false;
28312
28313
28314                 function someMissing() {
28315                     if (context.inIntro()) { return false; }
28316                     var osm = context.connection();
28317                     if (osm) {
28318                         var missing = _coords.filter(function(loc) { return !osm.isDataLoaded(loc); });
28319                         if (missing.length) {
28320                             missing.forEach(function(loc) { context.loadTileAtLoc(loc); });
28321                             return true;
28322                         }
28323                     }
28324                     return false;
28325                 }
28326             };
28327
28328
28329             operation.tooltip = function() {
28330                 var disable = operation.disabled();
28331                 return disable ?
28332                     _t('operations.circularize.' + disable + '.' + _amount) :
28333                     _t('operations.circularize.description.' + _amount);
28334             };
28335
28336
28337             operation.annotation = function() {
28338                 return _t('operations.circularize.annotation.' + _amount);
28339             };
28340
28341
28342             operation.id = 'circularize';
28343             operation.keys = [_t('operations.circularize.key')];
28344             operation.title = _t('operations.circularize.title');
28345             operation.behavior = behaviorOperation(context).which(operation);
28346
28347             return operation;
28348         }
28349
28350         // Translate a MacOS key command into the appropriate Windows/Linux equivalent.
28351         // For example, ⌘Z -> Ctrl+Z
28352         var uiCmd = function (code) {
28353             var detected = utilDetect();
28354
28355             if (detected.os === 'mac') {
28356                 return code;
28357             }
28358
28359             if (detected.os === 'win') {
28360                 if (code === '⌘⇧Z') { return 'Ctrl+Y'; }
28361             }
28362
28363             var result = '',
28364                 replacements = {
28365                     '⌘': 'Ctrl',
28366                     '⇧': 'Shift',
28367                     '⌥': 'Alt',
28368                     '⌫': 'Backspace',
28369                     '⌦': 'Delete'
28370                 };
28371
28372             for (var i = 0; i < code.length; i++) {
28373                 if (code[i] in replacements) {
28374                     result += replacements[code[i]] + (i < code.length - 1 ? '+' : '');
28375                 } else {
28376                     result += code[i];
28377                 }
28378             }
28379
28380             return result;
28381         };
28382
28383
28384         // return a display-focused string for a given keyboard code
28385         uiCmd.display = function(code) {
28386             if (code.length !== 1) { return code; }
28387
28388             var detected = utilDetect();
28389             var mac = (detected.os === 'mac');
28390             var replacements = {
28391                 '⌘': mac ? '⌘ ' + _t('shortcuts.key.cmd')    : _t('shortcuts.key.ctrl'),
28392                 '⇧': mac ? '⇧ ' + _t('shortcuts.key.shift')  : _t('shortcuts.key.shift'),
28393                 '⌥': mac ? '⌥ ' + _t('shortcuts.key.option') : _t('shortcuts.key.alt'),
28394                 '⌃': mac ? '⌃ ' + _t('shortcuts.key.ctrl')   : _t('shortcuts.key.ctrl'),
28395                 '⌫': mac ? '⌫ ' + _t('shortcuts.key.delete') : _t('shortcuts.key.backspace'),
28396                 '⌦': mac ? '⌦ ' + _t('shortcuts.key.del')    : _t('shortcuts.key.del'),
28397                 '↖': mac ? '↖ ' + _t('shortcuts.key.pgup')   : _t('shortcuts.key.pgup'),
28398                 '↘': mac ? '↘ ' + _t('shortcuts.key.pgdn')   : _t('shortcuts.key.pgdn'),
28399                 '⇞': mac ? '⇞ ' + _t('shortcuts.key.home')   : _t('shortcuts.key.home'),
28400                 '⇟': mac ? '⇟ ' + _t('shortcuts.key.end')    : _t('shortcuts.key.end'),
28401                 '↵': mac ? '⏎ ' + _t('shortcuts.key.return') : _t('shortcuts.key.enter'),
28402                 '⎋': mac ? '⎋ ' + _t('shortcuts.key.esc')    : _t('shortcuts.key.esc'),
28403                 '☰': mac ? '☰ ' + _t('shortcuts.key.menu')  : _t('shortcuts.key.menu'),
28404             };
28405
28406             return replacements[code] || code;
28407         };
28408
28409         function operationDelete(context, selectedIDs) {
28410             var multi = (selectedIDs.length === 1 ? 'single' : 'multiple');
28411             var action = actionDeleteMultiple(selectedIDs);
28412             var nodes = utilGetAllNodes(selectedIDs, context.graph());
28413             var coords = nodes.map(function(n) { return n.loc; });
28414             var extent = utilTotalExtent(selectedIDs, context.graph());
28415
28416
28417             var operation = function() {
28418                 var nextSelectedID;
28419                 var nextSelectedLoc;
28420
28421                 if (selectedIDs.length === 1) {
28422                     var id = selectedIDs[0];
28423                     var entity = context.entity(id);
28424                     var geometry = entity.geometry(context.graph());
28425                     var parents = context.graph().parentWays(entity);
28426                     var parent = parents[0];
28427
28428                     // Select the next closest node in the way.
28429                     if (geometry === 'vertex') {
28430                         var nodes = parent.nodes;
28431                         var i = nodes.indexOf(id);
28432
28433                         if (i === 0) {
28434                             i++;
28435                         } else if (i === nodes.length - 1) {
28436                             i--;
28437                         } else {
28438                             var a = geoSphericalDistance(entity.loc, context.entity(nodes[i - 1]).loc);
28439                             var b = geoSphericalDistance(entity.loc, context.entity(nodes[i + 1]).loc);
28440                             i = a < b ? i - 1 : i + 1;
28441                         }
28442
28443                         nextSelectedID = nodes[i];
28444                         nextSelectedLoc = context.entity(nextSelectedID).loc;
28445                     }
28446                 }
28447
28448                 context.perform(action, operation.annotation());
28449                 context.validator().validate();
28450
28451                 if (nextSelectedID && nextSelectedLoc) {
28452                     if (context.hasEntity(nextSelectedID)) {
28453                         context.enter(modeSelect(context, [nextSelectedID]).follow(true));
28454                     } else {
28455                         context.map().centerEase(nextSelectedLoc);
28456                         context.enter(modeBrowse(context));
28457                     }
28458                 } else {
28459                     context.enter(modeBrowse(context));
28460                 }
28461
28462             };
28463
28464
28465             operation.available = function() {
28466                 return true;
28467             };
28468
28469
28470             operation.disabled = function() {
28471                 if (extent.percentContainedIn(context.map().extent()) < 0.8) {
28472                     return 'too_large';
28473                 } else if (someMissing()) {
28474                     return 'not_downloaded';
28475                 } else if (selectedIDs.some(context.hasHiddenConnections)) {
28476                     return 'connected_to_hidden';
28477                 } else if (selectedIDs.some(protectedMember)) {
28478                     return 'part_of_relation';
28479                 } else if (selectedIDs.some(incompleteRelation)) {
28480                     return 'incomplete_relation';
28481                 } else if (selectedIDs.some(hasWikidataTag)) {
28482                     return 'has_wikidata_tag';
28483                 }
28484
28485                 return false;
28486
28487
28488                 function someMissing() {
28489                     if (context.inIntro()) { return false; }
28490                     var osm = context.connection();
28491                     if (osm) {
28492                         var missing = coords.filter(function(loc) { return !osm.isDataLoaded(loc); });
28493                         if (missing.length) {
28494                             missing.forEach(function(loc) { context.loadTileAtLoc(loc); });
28495                             return true;
28496                         }
28497                     }
28498                     return false;
28499                 }
28500
28501                 function hasWikidataTag(id) {
28502                     var entity = context.entity(id);
28503                     return entity.tags.wikidata && entity.tags.wikidata.trim().length > 0;
28504                 }
28505
28506                 function incompleteRelation(id) {
28507                     var entity = context.entity(id);
28508                     return entity.type === 'relation' && !entity.isComplete(context.graph());
28509                 }
28510
28511                 function protectedMember(id) {
28512                     var entity = context.entity(id);
28513                     if (entity.type !== 'way') { return false; }
28514
28515                     var parents = context.graph().parentRelations(entity);
28516                     for (var i = 0; i < parents.length; i++) {
28517                         var parent = parents[i];
28518                         var type = parent.tags.type;
28519                         var role = parent.memberById(id).role || 'outer';
28520                         if (type === 'route' || type === 'boundary' || (type === 'multipolygon' && role === 'outer')) {
28521                             return true;
28522                         }
28523                     }
28524                     return false;
28525                 }
28526             };
28527
28528
28529             operation.tooltip = function() {
28530                 var disable = operation.disabled();
28531                 return disable ?
28532                     _t('operations.delete.' + disable + '.' + multi) :
28533                     _t('operations.delete.description' + '.' + multi);
28534             };
28535
28536
28537             operation.annotation = function() {
28538                 return selectedIDs.length === 1 ?
28539                     _t('operations.delete.annotation.' + context.graph().geometry(selectedIDs[0])) :
28540                     _t('operations.delete.annotation.multiple', { n: selectedIDs.length });
28541             };
28542
28543
28544             operation.id = 'delete';
28545             operation.keys = [uiCmd('⌘⌫'), uiCmd('⌘⌦'), uiCmd('⌦')];
28546             operation.title = _t('operations.delete.title');
28547             operation.behavior = behaviorOperation(context).which(operation);
28548
28549             return operation;
28550         }
28551
28552         function operationOrthogonalize(context, selectedIDs) {
28553             var _extent;
28554             var _type;
28555             var _actions = selectedIDs.map(chooseAction).filter(Boolean);
28556             var _amount = _actions.length === 1 ? 'single' : 'multiple';
28557             var _coords = utilGetAllNodes(selectedIDs, context.graph())
28558                 .map(function(n) { return n.loc; });
28559
28560
28561             function chooseAction(entityID) {
28562
28563                 var entity = context.entity(entityID);
28564                 var geometry = entity.geometry(context.graph());
28565
28566                 if (!_extent) {
28567                     _extent =  entity.extent(context.graph());
28568                 } else {
28569                     _extent = _extent.extend(entity.extent(context.graph()));
28570                 }
28571
28572                 // square a line/area
28573                 if (entity.type === 'way' && new Set(entity.nodes).size > 2 ) {
28574                     if (_type && _type !== 'feature') { return null; }
28575                     _type = 'feature';
28576                     return actionOrthogonalize(entityID, context.projection);
28577
28578                 // square a single vertex
28579                 } else if (geometry === 'vertex') {
28580                     if (_type && _type !== 'corner') { return null; }
28581                     _type = 'corner';
28582                     var graph = context.graph();
28583                     var parents = graph.parentWays(entity);
28584                     if (parents.length === 1) {
28585                         var way = parents[0];
28586                         if (way.nodes.indexOf(entityID) !== -1) {
28587                             return actionOrthogonalize(way.id, context.projection, entityID);
28588                         }
28589                     }
28590                 }
28591
28592                 return null;
28593             }
28594
28595
28596             var operation = function() {
28597                 if (!_actions.length) { return; }
28598
28599                 var combinedAction = function(graph, t) {
28600                     _actions.forEach(function(action) {
28601                         if (!action.disabled(graph)) {
28602                             graph = action(graph, t);
28603                         }
28604                     });
28605                     return graph;
28606                 };
28607                 combinedAction.transitionable = true;
28608
28609                 context.perform(combinedAction, operation.annotation());
28610
28611                 window.setTimeout(function() {
28612                     context.validator().validate();
28613                 }, 300);  // after any transition
28614             };
28615
28616
28617             operation.available = function() {
28618                 return _actions.length && selectedIDs.length === _actions.length;
28619             };
28620
28621
28622             // don't cache this because the visible extent could change
28623             operation.disabled = function() {
28624                 if (!_actions.length) { return ''; }
28625
28626                 var actionDisableds = _actions.map(function(action) {
28627                     return action.disabled(context.graph());
28628                 }).filter(Boolean);
28629
28630                 if (actionDisableds.length === _actions.length) {
28631                     // none of the features can be squared
28632
28633                     if (new Set(actionDisableds).size > 1) {
28634                         return 'multiple_blockers';
28635                     }
28636                     return actionDisableds[0];
28637                 } else if (_extent &&
28638                            _extent.percentContainedIn(context.map().extent()) < 0.8) {
28639                     return 'too_large';
28640                 } else if (someMissing()) {
28641                     return 'not_downloaded';
28642                 } else if (selectedIDs.some(context.hasHiddenConnections)) {
28643                     return 'connected_to_hidden';
28644                 }
28645
28646                 return false;
28647
28648
28649                 function someMissing() {
28650                     if (context.inIntro()) { return false; }
28651                     var osm = context.connection();
28652                     if (osm) {
28653                         var missing = _coords.filter(function(loc) { return !osm.isDataLoaded(loc); });
28654                         if (missing.length) {
28655                             missing.forEach(function(loc) { context.loadTileAtLoc(loc); });
28656                             return true;
28657                         }
28658                     }
28659                     return false;
28660                 }
28661             };
28662
28663
28664             operation.tooltip = function() {
28665                 var disable = operation.disabled();
28666                 return disable ?
28667                     _t('operations.orthogonalize.' + disable + '.' + _amount) :
28668                     _t('operations.orthogonalize.description.' + _type + '.' + _amount);
28669             };
28670
28671
28672             operation.annotation = function() {
28673                 return _t('operations.orthogonalize.annotation.' + _type + '.' + _amount);
28674             };
28675
28676
28677             operation.id = 'orthogonalize';
28678             operation.keys = [_t('operations.orthogonalize.key')];
28679             operation.title = _t('operations.orthogonalize.title');
28680             operation.behavior = behaviorOperation(context).which(operation);
28681
28682             return operation;
28683         }
28684
28685         function operationReflectShort(context, selectedIDs) {
28686             return operationReflect(context, selectedIDs, 'short');
28687         }
28688
28689
28690         function operationReflectLong(context, selectedIDs) {
28691             return operationReflect(context, selectedIDs, 'long');
28692         }
28693
28694
28695         function operationReflect(context, selectedIDs, axis) {
28696             axis = axis || 'long';
28697             var multi = (selectedIDs.length === 1 ? 'single' : 'multiple');
28698             var nodes = utilGetAllNodes(selectedIDs, context.graph());
28699             var coords = nodes.map(function(n) { return n.loc; });
28700             var extent = utilTotalExtent(selectedIDs, context.graph());
28701
28702
28703             var operation = function() {
28704                 var action = actionReflect(selectedIDs, context.projection)
28705                     .useLongAxis(Boolean(axis === 'long'));
28706
28707                 context.perform(action, operation.annotation());
28708
28709                 window.setTimeout(function() {
28710                     context.validator().validate();
28711                 }, 300);  // after any transition
28712             };
28713
28714
28715             operation.available = function() {
28716                 return nodes.length >= 3;
28717             };
28718
28719
28720             // don't cache this because the visible extent could change
28721             operation.disabled = function() {
28722                 if (extent.percentContainedIn(context.map().extent()) < 0.8) {
28723                     return 'too_large';
28724                 } else if (someMissing()) {
28725                     return 'not_downloaded';
28726                 } else if (selectedIDs.some(context.hasHiddenConnections)) {
28727                     return 'connected_to_hidden';
28728                 } else if (selectedIDs.some(incompleteRelation)) {
28729                     return 'incomplete_relation';
28730                 }
28731
28732                 return false;
28733
28734
28735                 function someMissing() {
28736                     if (context.inIntro()) { return false; }
28737                     var osm = context.connection();
28738                     if (osm) {
28739                         var missing = coords.filter(function(loc) { return !osm.isDataLoaded(loc); });
28740                         if (missing.length) {
28741                             missing.forEach(function(loc) { context.loadTileAtLoc(loc); });
28742                             return true;
28743                         }
28744                     }
28745                     return false;
28746                 }
28747
28748                 function incompleteRelation(id) {
28749                     var entity = context.entity(id);
28750                     return entity.type === 'relation' && !entity.isComplete(context.graph());
28751                 }
28752             };
28753
28754
28755             operation.tooltip = function() {
28756                 var disable = operation.disabled();
28757                 return disable ?
28758                     _t('operations.reflect.' + disable + '.' + multi) :
28759                     _t('operations.reflect.description.' + axis + '.' + multi);
28760             };
28761
28762
28763             operation.annotation = function() {
28764                 return _t('operations.reflect.annotation.' + axis + '.' + multi);
28765             };
28766
28767
28768             operation.id = 'reflect-' + axis;
28769             operation.keys = [_t('operations.reflect.key.' + axis)];
28770             operation.title = _t('operations.reflect.title.' + axis);
28771             operation.behavior = behaviorOperation(context).which(operation);
28772
28773             return operation;
28774         }
28775
28776         function operationMove(context, selectedIDs) {
28777             var multi = (selectedIDs.length === 1 ? 'single' : 'multiple');
28778             var nodes = utilGetAllNodes(selectedIDs, context.graph());
28779             var coords = nodes.map(function(n) { return n.loc; });
28780             var extent = utilTotalExtent(selectedIDs, context.graph());
28781
28782
28783             var operation = function() {
28784                 context.enter(modeMove(context, selectedIDs));
28785             };
28786
28787
28788             operation.available = function() {
28789                 return selectedIDs.length > 1 ||
28790                     context.entity(selectedIDs[0]).type !== 'node';
28791             };
28792
28793
28794             operation.disabled = function() {
28795                 if (extent.percentContainedIn(context.map().extent()) < 0.8) {
28796                     return 'too_large';
28797                 } else if (someMissing()) {
28798                     return 'not_downloaded';
28799                 } else if (selectedIDs.some(context.hasHiddenConnections)) {
28800                     return 'connected_to_hidden';
28801                 } else if (selectedIDs.some(incompleteRelation)) {
28802                     return 'incomplete_relation';
28803                 }
28804
28805                 return false;
28806
28807
28808                 function someMissing() {
28809                     if (context.inIntro()) { return false; }
28810                     var osm = context.connection();
28811                     if (osm) {
28812                         var missing = coords.filter(function(loc) { return !osm.isDataLoaded(loc); });
28813                         if (missing.length) {
28814                             missing.forEach(function(loc) { context.loadTileAtLoc(loc); });
28815                             return true;
28816                         }
28817                     }
28818                     return false;
28819                 }
28820
28821                 function incompleteRelation(id) {
28822                     var entity = context.entity(id);
28823                     return entity.type === 'relation' && !entity.isComplete(context.graph());
28824                 }
28825             };
28826
28827
28828             operation.tooltip = function() {
28829                 var disable = operation.disabled();
28830                 return disable ?
28831                     _t('operations.move.' + disable + '.' + multi) :
28832                     _t('operations.move.description.' + multi);
28833             };
28834
28835
28836             operation.annotation = function() {
28837                 return selectedIDs.length === 1 ?
28838                     _t('operations.move.annotation.' + context.graph().geometry(selectedIDs[0])) :
28839                     _t('operations.move.annotation.multiple');
28840             };
28841
28842
28843             operation.id = 'move';
28844             operation.keys = [_t('operations.move.key')];
28845             operation.title = _t('operations.move.title');
28846             operation.behavior = behaviorOperation(context).which(operation);
28847
28848             operation.mouseOnly = true;
28849
28850             return operation;
28851         }
28852
28853         function modeRotate(context, entityIDs) {
28854             var mode = {
28855                 id: 'rotate',
28856                 button: 'browse'
28857             };
28858
28859             var keybinding = utilKeybinding('rotate');
28860             var behaviors = [
28861                 behaviorEdit(context),
28862                 operationCircularize(context, entityIDs).behavior,
28863                 operationDelete(context, entityIDs).behavior,
28864                 operationMove(context, entityIDs).behavior,
28865                 operationOrthogonalize(context, entityIDs).behavior,
28866                 operationReflectLong(context, entityIDs).behavior,
28867                 operationReflectShort(context, entityIDs).behavior
28868             ];
28869             var annotation = entityIDs.length === 1 ?
28870                 _t('operations.rotate.annotation.' + context.graph().geometry(entityIDs[0])) :
28871                 _t('operations.rotate.annotation.multiple');
28872
28873             var _prevGraph;
28874             var _prevAngle;
28875             var _prevTransform;
28876             var _pivot;
28877
28878
28879             function doRotate() {
28880                 var fn;
28881                 if (context.graph() !== _prevGraph) {
28882                     fn = context.perform;
28883                 } else {
28884                     fn = context.replace;
28885                 }
28886
28887                 // projection changed, recalculate _pivot
28888                 var projection = context.projection;
28889                 var currTransform = projection.transform();
28890                 if (!_prevTransform ||
28891                     currTransform.k !== _prevTransform.k ||
28892                     currTransform.x !== _prevTransform.x ||
28893                     currTransform.y !== _prevTransform.y) {
28894
28895                     var nodes = utilGetAllNodes(entityIDs, context.graph());
28896                     var points = nodes.map(function(n) { return projection(n.loc); });
28897                     _pivot = getPivot(points);
28898                     _prevAngle = undefined;
28899                 }
28900
28901
28902                 var currMouse = context.map().mouse();
28903                 var currAngle = Math.atan2(currMouse[1] - _pivot[1], currMouse[0] - _pivot[0]);
28904
28905                 if (typeof _prevAngle === 'undefined') { _prevAngle = currAngle; }
28906                 var delta = currAngle - _prevAngle;
28907
28908                 fn(actionRotate(entityIDs, _pivot, delta, projection));
28909
28910                 _prevTransform = currTransform;
28911                 _prevAngle = currAngle;
28912                 _prevGraph = context.graph();
28913             }
28914
28915             function getPivot(points) {
28916                 var _pivot;
28917                 if (points.length === 1) {
28918                     _pivot = points[0];
28919                 } else if (points.length === 2) {
28920                     _pivot = geoVecInterp(points[0], points[1], 0.5);
28921                 } else {
28922                     var polygonHull = d3_polygonHull(points);
28923                     if (polygonHull.length === 2) {
28924                         _pivot = geoVecInterp(points[0], points[1], 0.5);
28925                     } else {
28926                         _pivot = d3_polygonCentroid(d3_polygonHull(points));
28927                     }
28928                 }
28929                 return _pivot;
28930             }
28931
28932
28933             function finish() {
28934                 event.stopPropagation();
28935                 context.replace(actionNoop(), annotation);
28936                 context.enter(modeSelect(context, entityIDs));
28937             }
28938
28939
28940             function cancel() {
28941                 context.pop();
28942                 context.enter(modeSelect(context, entityIDs));
28943             }
28944
28945
28946             function undone() {
28947                 context.enter(modeBrowse(context));
28948             }
28949
28950
28951             mode.enter = function() {
28952                 context.features().forceVisible(entityIDs);
28953
28954                 behaviors.forEach(context.install);
28955
28956                 context.surface()
28957                     .on('mousemove.rotate', doRotate)
28958                     .on('click.rotate', finish);
28959
28960                 context.history()
28961                     .on('undone.rotate', undone);
28962
28963                 keybinding
28964                     .on('⎋', cancel)
28965                     .on('↩', finish);
28966
28967                 select(document)
28968                     .call(keybinding);
28969             };
28970
28971
28972             mode.exit = function() {
28973                 behaviors.forEach(context.uninstall);
28974
28975                 context.surface()
28976                     .on('mousemove.rotate', null)
28977                     .on('click.rotate', null);
28978
28979                 context.history()
28980                     .on('undone.rotate', null);
28981
28982                 select(document)
28983                     .call(keybinding.unbind);
28984
28985                 context.features().forceVisible([]);
28986             };
28987
28988
28989             mode.selectedIDs = function() {
28990                 if (!arguments.length) { return entityIDs; }
28991                 // no assign
28992                 return mode;
28993             };
28994
28995
28996             return mode;
28997         }
28998
28999         function operationRotate(context, selectedIDs) {
29000             var multi = (selectedIDs.length === 1 ? 'single' : 'multiple');
29001             var nodes = utilGetAllNodes(selectedIDs, context.graph());
29002             var coords = nodes.map(function(n) { return n.loc; });
29003             var extent = utilTotalExtent(selectedIDs, context.graph());
29004
29005
29006             var operation = function() {
29007                 context.enter(modeRotate(context, selectedIDs));
29008             };
29009
29010
29011             operation.available = function() {
29012                 return nodes.length >= 2;
29013             };
29014
29015
29016             operation.disabled = function() {
29017
29018                 if (extent.percentContainedIn(context.map().extent()) < 0.8) {
29019                     return 'too_large';
29020                 } else if (someMissing()) {
29021                     return 'not_downloaded';
29022                 } else if (selectedIDs.some(context.hasHiddenConnections)) {
29023                     return 'connected_to_hidden';
29024                 } else if (selectedIDs.some(incompleteRelation)) {
29025                     return 'incomplete_relation';
29026                 }
29027
29028                 return false;
29029
29030
29031                 function someMissing() {
29032                     if (context.inIntro()) { return false; }
29033                     var osm = context.connection();
29034                     if (osm) {
29035                         var missing = coords.filter(function(loc) { return !osm.isDataLoaded(loc); });
29036                         if (missing.length) {
29037                             missing.forEach(function(loc) { context.loadTileAtLoc(loc); });
29038                             return true;
29039                         }
29040                     }
29041                     return false;
29042                 }
29043
29044                 function incompleteRelation(id) {
29045                     var entity = context.entity(id);
29046                     return entity.type === 'relation' && !entity.isComplete(context.graph());
29047                 }
29048             };
29049
29050
29051             operation.tooltip = function() {
29052                 var disable = operation.disabled();
29053                 return disable ?
29054                     _t('operations.rotate.' + disable + '.' + multi) :
29055                     _t('operations.rotate.description.' + multi);
29056             };
29057
29058
29059             operation.annotation = function() {
29060                 return selectedIDs.length === 1 ?
29061                     _t('operations.rotate.annotation.' + context.graph().geometry(selectedIDs[0])) :
29062                     _t('operations.rotate.annotation.multiple');
29063             };
29064
29065
29066             operation.id = 'rotate';
29067             operation.keys = [_t('operations.rotate.key')];
29068             operation.title = _t('operations.rotate.title');
29069             operation.behavior = behaviorOperation(context).which(operation);
29070
29071             operation.mouseOnly = true;
29072
29073             return operation;
29074         }
29075
29076         function modeMove(context, entityIDs, baseGraph) {
29077             var mode = {
29078                 id: 'move',
29079                 button: 'browse'
29080             };
29081
29082             var keybinding = utilKeybinding('move');
29083             var behaviors = [
29084                 behaviorEdit(context),
29085                 operationCircularize(context, entityIDs).behavior,
29086                 operationDelete(context, entityIDs).behavior,
29087                 operationOrthogonalize(context, entityIDs).behavior,
29088                 operationReflectLong(context, entityIDs).behavior,
29089                 operationReflectShort(context, entityIDs).behavior,
29090                 operationRotate(context, entityIDs).behavior
29091             ];
29092             var annotation = entityIDs.length === 1 ?
29093                 _t('operations.move.annotation.' + context.graph().geometry(entityIDs[0])) :
29094                 _t('operations.move.annotation.multiple');
29095
29096             var _prevGraph;
29097             var _cache;
29098             var _origin;
29099             var _nudgeInterval;
29100
29101
29102             function doMove(nudge) {
29103                 nudge = nudge || [0, 0];
29104
29105                 var fn;
29106                 if (_prevGraph !== context.graph()) {
29107                     _cache = {};
29108                     _origin = context.map().mouseCoordinates();
29109                     fn = context.perform;
29110                 } else {
29111                     fn = context.overwrite;
29112                 }
29113
29114                 var currMouse = context.map().mouse();
29115                 var origMouse = context.projection(_origin);
29116                 var delta = geoVecSubtract(geoVecSubtract(currMouse, origMouse), nudge);
29117
29118                 fn(actionMove(entityIDs, delta, context.projection, _cache));
29119                 _prevGraph = context.graph();
29120             }
29121
29122
29123             function startNudge(nudge) {
29124                 if (_nudgeInterval) { window.clearInterval(_nudgeInterval); }
29125                 _nudgeInterval = window.setInterval(function() {
29126                     context.map().pan(nudge);
29127                     doMove(nudge);
29128                 }, 50);
29129             }
29130
29131
29132             function stopNudge() {
29133                 if (_nudgeInterval) {
29134                     window.clearInterval(_nudgeInterval);
29135                     _nudgeInterval = null;
29136                 }
29137             }
29138
29139
29140             function move() {
29141                 doMove();
29142                 var nudge = geoViewportEdge(context.map().mouse(), context.map().dimensions());
29143                 if (nudge) {
29144                     startNudge(nudge);
29145                 } else {
29146                     stopNudge();
29147                 }
29148             }
29149
29150
29151             function finish() {
29152                 event.stopPropagation();
29153                 context.replace(actionNoop(), annotation);
29154                 context.enter(modeSelect(context, entityIDs));
29155                 stopNudge();
29156             }
29157
29158
29159             function cancel() {
29160                 if (baseGraph) {
29161                     while (context.graph() !== baseGraph) { context.pop(); }
29162                     context.enter(modeBrowse(context));
29163                 } else {
29164                     context.pop();
29165                     context.enter(modeSelect(context, entityIDs));
29166                 }
29167                 stopNudge();
29168             }
29169
29170
29171             function undone() {
29172                 context.enter(modeBrowse(context));
29173             }
29174
29175
29176             mode.enter = function() {
29177                 _origin = context.map().mouseCoordinates();
29178                 _prevGraph = null;
29179                 _cache = {};
29180
29181                 context.features().forceVisible(entityIDs);
29182
29183                 behaviors.forEach(context.install);
29184
29185                 context.surface()
29186                     .on('mousemove.move', move)
29187                     .on('click.move', finish);
29188
29189                 context.history()
29190                     .on('undone.move', undone);
29191
29192                 keybinding
29193                     .on('⎋', cancel)
29194                     .on('↩', finish);
29195
29196                 select(document)
29197                     .call(keybinding);
29198             };
29199
29200
29201             mode.exit = function() {
29202                 stopNudge();
29203
29204                 behaviors.forEach(function(behavior) {
29205                     context.uninstall(behavior);
29206                 });
29207
29208                 context.surface()
29209                     .on('mousemove.move', null)
29210                     .on('click.move', null);
29211
29212                 context.history()
29213                     .on('undone.move', null);
29214
29215                 select(document)
29216                     .call(keybinding.unbind);
29217
29218                 context.features().forceVisible([]);
29219             };
29220
29221
29222             mode.selectedIDs = function() {
29223                 if (!arguments.length) { return entityIDs; }
29224                 // no assign
29225                 return mode;
29226             };
29227
29228
29229             return mode;
29230         }
29231
29232         // see also `operationPaste`
29233         function behaviorPaste(context) {
29234
29235             function doPaste() {
29236                 // prevent paste during low zoom selection
29237                 if (!context.map().withinEditableZoom()) { return; }
29238
29239                 event.preventDefault();
29240
29241                 var baseGraph = context.graph();
29242                 var mouse = context.map().mouse();
29243                 var projection = context.projection;
29244                 var viewport = geoExtent(projection.clipExtent()).polygon();
29245
29246                 if (!geoPointInPolygon(mouse, viewport)) { return; }
29247
29248                 var oldIDs = context.copyIDs();
29249                 if (!oldIDs.length) { return; }
29250
29251                 var extent = geoExtent();
29252                 var oldGraph = context.copyGraph();
29253                 var newIDs = [];
29254
29255                 var action = actionCopyEntities(oldIDs, oldGraph);
29256                 context.perform(action);
29257
29258                 var copies = action.copies();
29259                 var originals = new Set();
29260                 Object.values(copies).forEach(function(entity) { originals.add(entity.id); });
29261
29262                 for (var id in copies) {
29263                     var oldEntity = oldGraph.entity(id);
29264                     var newEntity = copies[id];
29265
29266                     extent._extend(oldEntity.extent(oldGraph));
29267
29268                     // Exclude child nodes from newIDs if their parent way was also copied.
29269                     var parents = context.graph().parentWays(newEntity);
29270                     var parentCopied = parents.some(function(parent) {
29271                         return originals.has(parent.id);
29272                     });
29273
29274                     if (!parentCopied) {
29275                         newIDs.push(newEntity.id);
29276                     }
29277                 }
29278
29279                 // Put pasted objects where mouse pointer is..
29280                 var copyPoint = (context.copyLonLat() && projection(context.copyLonLat())) || projection(extent.center());
29281                 var delta = geoVecSubtract(mouse, copyPoint);
29282
29283                 context.perform(actionMove(newIDs, delta, projection));
29284                 context.enter(modeMove(context, newIDs, baseGraph));
29285             }
29286
29287
29288             function behavior() {
29289                 context.keybinding().on(uiCmd('⌘V'), doPaste);
29290                 return behavior;
29291             }
29292
29293
29294             behavior.off = function() {
29295                 context.keybinding().off(uiCmd('⌘V'));
29296             };
29297
29298
29299             return behavior;
29300         }
29301
29302         /*
29303             `behaviorDrag` is like `d3_behavior.drag`, with the following differences:
29304
29305             * The `origin` function is expected to return an [x, y] tuple rather than an
29306               {x, y} object.
29307             * The events are `start`, `move`, and `end`.
29308               (https://github.com/mbostock/d3/issues/563)
29309             * The `start` event is not dispatched until the first cursor movement occurs.
29310               (https://github.com/mbostock/d3/pull/368)
29311             * The `move` event has a `point` and `delta` [x, y] tuple properties rather
29312               than `x`, `y`, `dx`, and `dy` properties.
29313             * The `end` event is not dispatched if no movement occurs.
29314             * An `off` function is available that unbinds the drag's internal event handlers.
29315          */
29316
29317         function behaviorDrag() {
29318             var dispatch$1 = dispatch('start', 'move', 'end');
29319
29320             // see also behaviorSelect
29321             var _tolerancePx = 1; // keep this low to facilitate pixel-perfect micromapping
29322             var _penTolerancePx = 4; // styluses can be touchy so require greater movement - #1981
29323
29324             var _origin = null;
29325             var _selector = '';
29326             var _event;
29327             var _target;
29328             var _surface;
29329             var _pointerId;
29330
29331             // use pointer events on supported platforms; fallback to mouse events
29332             var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
29333
29334             var d3_event_userSelectProperty = utilPrefixCSSProperty('UserSelect');
29335             var d3_event_userSelectSuppress = function() {
29336                     var selection$1 = selection();
29337                     var select = selection$1.style(d3_event_userSelectProperty);
29338                     selection$1.style(d3_event_userSelectProperty, 'none');
29339                     return function() {
29340                         selection$1.style(d3_event_userSelectProperty, select);
29341                     };
29342                 };
29343
29344
29345             function eventOf(thiz, argumentz) {
29346                 return function(e1) {
29347                     e1.target = behavior;
29348                     customEvent(e1, dispatch$1.apply, dispatch$1, [e1.type, thiz, argumentz]);
29349                 };
29350             }
29351
29352
29353             function pointerdown() {
29354
29355                 if (_pointerId) { return; }
29356
29357                 _pointerId = event.pointerId || 'mouse';
29358
29359                 _target = this;
29360                 _event = eventOf(_target, arguments);
29361
29362                 // only force reflow once per drag
29363                 var pointerLocGetter = utilFastMouse(_surface || _target.parentNode);
29364
29365                 var offset;
29366                 var startOrigin = pointerLocGetter(event);
29367                 var started = false;
29368                 var selectEnable = d3_event_userSelectSuppress();
29369
29370                 select(window)
29371                     .on(_pointerPrefix + 'move.drag', pointermove)
29372                     .on(_pointerPrefix + 'up.drag pointercancel.drag', pointerup, true);
29373
29374                 if (_origin) {
29375                     offset = _origin.apply(_target, arguments);
29376                     offset = [offset[0] - startOrigin[0], offset[1] - startOrigin[1]];
29377                 } else {
29378                     offset = [0, 0];
29379                 }
29380
29381                 event.stopPropagation();
29382
29383
29384                 function pointermove() {
29385                     if (_pointerId !== (event.pointerId || 'mouse')) { return; }
29386
29387                     var p = pointerLocGetter(event);
29388
29389                     if (!started) {
29390                         var dist = geoVecLength(startOrigin,  p);
29391                         var tolerance = event.pointerType === 'pen' ? _penTolerancePx : _tolerancePx;
29392                         // don't start until the drag has actually moved somewhat
29393                         if (dist < tolerance) { return; }
29394
29395                         started = true;
29396                         _event({ type: 'start' });
29397
29398                     // Don't send a `move` event in the same cycle as `start` since dragging
29399                     // a midpoint will convert the target to a node.
29400                     } else {
29401
29402                         startOrigin = p;
29403                         event.stopPropagation();
29404                         event.preventDefault();
29405
29406                         var dx = p[0] - startOrigin[0];
29407                         var dy = p[1] - startOrigin[1];
29408                         _event({
29409                             type: 'move',
29410                             point: [p[0] + offset[0],  p[1] + offset[1]],
29411                             delta: [dx, dy]
29412                         });
29413                     }
29414                 }
29415
29416
29417                 function pointerup() {
29418                     if (_pointerId !== (event.pointerId || 'mouse')) { return; }
29419
29420                     _pointerId = null;
29421
29422                     if (started) {
29423                         _event({ type: 'end' });
29424
29425                         event.preventDefault();
29426                     }
29427
29428                     select(window)
29429                         .on(_pointerPrefix + 'move.drag', null)
29430                         .on(_pointerPrefix + 'up.drag pointercancel.drag', null);
29431
29432                     selectEnable();
29433                 }
29434             }
29435
29436
29437             function behavior(selection) {
29438                 _pointerId = null;
29439                 var matchesSelector = utilPrefixDOMProperty('matchesSelector');
29440                 var delegate = pointerdown;
29441
29442                 if (_selector) {
29443                     delegate = function() {
29444                         var root = this;
29445                         var target = event.target;
29446                         for (; target && target !== root; target = target.parentNode) {
29447                             var datum = target.__data__;
29448
29449                             var entity = datum instanceof osmNote ? datum
29450                                 : datum && datum.properties && datum.properties.entity;
29451
29452                             if (entity && target[matchesSelector](_selector)) {
29453                                 return pointerdown.call(target, entity);
29454                             }
29455                         }
29456                     };
29457                 }
29458
29459                 selection
29460                     .on(_pointerPrefix + 'down.drag' + _selector, delegate);
29461             }
29462
29463
29464             behavior.off = function(selection) {
29465                 selection
29466                     .on(_pointerPrefix + 'down.drag' + _selector, null);
29467             };
29468
29469
29470             behavior.selector = function(_) {
29471                 if (!arguments.length) { return _selector; }
29472                 _selector = _;
29473                 return behavior;
29474             };
29475
29476
29477             behavior.origin = function(_) {
29478                 if (!arguments.length) { return _origin; }
29479                 _origin = _;
29480                 return behavior;
29481             };
29482
29483
29484             behavior.cancel = function() {
29485                 select(window)
29486                     .on(_pointerPrefix + 'move.drag', null)
29487                     .on(_pointerPrefix + 'up.drag pointercancel.drag', null);
29488                 return behavior;
29489             };
29490
29491
29492             behavior.target = function() {
29493                 if (!arguments.length) { return _target; }
29494                 _target = arguments[0];
29495                 _event = eventOf(_target, Array.prototype.slice.call(arguments, 1));
29496                 return behavior;
29497             };
29498
29499
29500             behavior.surface = function() {
29501                 if (!arguments.length) { return _surface; }
29502                 _surface = arguments[0];
29503                 return behavior;
29504             };
29505
29506
29507             return utilRebind(behavior, dispatch$1, 'on');
29508         }
29509
29510         function modeDragNode(context) {
29511             var mode = {
29512                 id: 'drag-node',
29513                 button: 'browse'
29514             };
29515             var hover = behaviorHover(context).altDisables(true)
29516                 .on('hover', context.ui().sidebar.hover);
29517             var edit = behaviorEdit(context);
29518
29519             var _nudgeInterval;
29520             var _restoreSelectedIDs = [];
29521             var _wasMidpoint = false;
29522             var _isCancelled = false;
29523             var _activeEntity;
29524             var _startLoc;
29525             var _lastLoc;
29526
29527
29528             function startNudge(entity, nudge) {
29529                 if (_nudgeInterval) { window.clearInterval(_nudgeInterval); }
29530                 _nudgeInterval = window.setInterval(function() {
29531                     context.map().pan(nudge);
29532                     doMove(entity, nudge);
29533                 }, 50);
29534             }
29535
29536
29537             function stopNudge() {
29538                 if (_nudgeInterval) {
29539                     window.clearInterval(_nudgeInterval);
29540                     _nudgeInterval = null;
29541                 }
29542             }
29543
29544
29545             function moveAnnotation(entity) {
29546                 return _t('operations.move.annotation.' + entity.geometry(context.graph()));
29547             }
29548
29549
29550             function connectAnnotation(nodeEntity, targetEntity) {
29551                 var nodeGeometry = nodeEntity.geometry(context.graph());
29552                 var targetGeometry = targetEntity.geometry(context.graph());
29553                 if (nodeGeometry === 'vertex' && targetGeometry === 'vertex') {
29554                     var nodeParentWayIDs = context.graph().parentWays(nodeEntity);
29555                     var targetParentWayIDs = context.graph().parentWays(targetEntity);
29556                     var sharedParentWays = utilArrayIntersection(nodeParentWayIDs, targetParentWayIDs);
29557                     // if both vertices are part of the same way
29558                     if (sharedParentWays.length !== 0) {
29559                         // if the nodes are next to each other, they are merged
29560                         if (sharedParentWays[0].areAdjacent(nodeEntity.id, targetEntity.id)) {
29561                             return _t('operations.connect.annotation.from_vertex.to_adjacent_vertex');
29562                         }
29563                         return _t('operations.connect.annotation.from_vertex.to_sibling_vertex');
29564                     }
29565                 }
29566                 return _t('operations.connect.annotation.from_' + nodeGeometry + '.to_' + targetGeometry);
29567             }
29568
29569
29570             function shouldSnapToNode(target) {
29571                 if (!_activeEntity) { return false; }
29572                 return _activeEntity.geometry(context.graph()) !== 'vertex' ||
29573                     (target.geometry(context.graph()) === 'vertex' || _mainPresetIndex.allowsVertex(target, context.graph()));
29574             }
29575
29576
29577             function origin(entity) {
29578                 return context.projection(entity.loc);
29579             }
29580
29581
29582             function keydown() {
29583                 if (event.keyCode === utilKeybinding.modifierCodes.alt) {
29584                     if (context.surface().classed('nope')) {
29585                         context.surface()
29586                             .classed('nope-suppressed', true);
29587                     }
29588                     context.surface()
29589                         .classed('nope', false)
29590                         .classed('nope-disabled', true);
29591                 }
29592             }
29593
29594
29595             function keyup() {
29596                 if (event.keyCode === utilKeybinding.modifierCodes.alt) {
29597                     if (context.surface().classed('nope-suppressed')) {
29598                         context.surface()
29599                             .classed('nope', true);
29600                     }
29601                     context.surface()
29602                         .classed('nope-suppressed', false)
29603                         .classed('nope-disabled', false);
29604                 }
29605             }
29606
29607
29608             function start(entity) {
29609                 _wasMidpoint = entity.type === 'midpoint';
29610                 var hasHidden = context.features().hasHiddenConnections(entity, context.graph());
29611                 _isCancelled = !context.editable() || event.sourceEvent.shiftKey || hasHidden;
29612
29613
29614                 if (_isCancelled) {
29615                     if (hasHidden) {
29616                         context.ui().flash
29617                             .duration(4000)
29618                             .text(_t('modes.drag_node.connected_to_hidden'))();
29619                     }
29620                     return drag.cancel();
29621                 }
29622
29623                 if (_wasMidpoint) {
29624                     var midpoint = entity;
29625                     entity = osmNode();
29626                     context.perform(actionAddMidpoint(midpoint, entity));
29627                     entity = context.entity(entity.id);   // get post-action entity
29628
29629                     var vertex = context.surface().selectAll('.' + entity.id);
29630                     drag.target(vertex.node(), entity);
29631
29632                 } else {
29633                     context.perform(actionNoop());
29634                 }
29635
29636                 _activeEntity = entity;
29637                 _startLoc = entity.loc;
29638
29639                 hover.ignoreVertex(entity.geometry(context.graph()) === 'vertex');
29640
29641                 context.surface().selectAll('.' + _activeEntity.id)
29642                     .classed('active', true);
29643
29644                 context.enter(mode);
29645             }
29646
29647
29648             // related code
29649             // - `behavior/draw.js` `datum()`
29650             function datum() {
29651                 var event$1 = event && event.sourceEvent;
29652                 if (!event$1 || event$1.altKey) {
29653                     return {};
29654                 } else {
29655                     // When dragging, snap only to touch targets..
29656                     // (this excludes area fills and active drawing elements)
29657                     var d = event$1.target.__data__;
29658                     return (d && d.properties && d.properties.target) ? d : {};
29659                 }
29660             }
29661
29662
29663             function doMove(entity, nudge) {
29664                 nudge = nudge || [0, 0];
29665
29666                 var currPoint = (event && event.point) || context.projection(_lastLoc);
29667                 var currMouse = geoVecSubtract(currPoint, nudge);
29668                 var loc = context.projection.invert(currMouse);
29669
29670                 if (!_nudgeInterval) {   // If not nudging at the edge of the viewport, try to snap..
29671                     // related code
29672                     // - `mode/drag_node.js`     `doMove()`
29673                     // - `behavior/draw.js`      `click()`
29674                     // - `behavior/draw_way.js`  `move()`
29675                     var d = datum();
29676                     var target = d && d.properties && d.properties.entity;
29677                     var targetLoc = target && target.loc;
29678                     var targetNodes = d && d.properties && d.properties.nodes;
29679                     var edge;
29680
29681                     if (targetLoc) {   // snap to node/vertex - a point target with `.loc`
29682                         if (shouldSnapToNode(target)) {
29683                             loc = targetLoc;
29684                         }
29685
29686                     } else if (targetNodes) {   // snap to way - a line target with `.nodes`
29687                         edge = geoChooseEdge(targetNodes, context.map().mouse(), context.projection, end.id);
29688                         if (edge) {
29689                             loc = edge.loc;
29690                         }
29691                     }
29692                 }
29693
29694                 context.replace(
29695                     actionMoveNode(entity.id, loc)
29696                 );
29697
29698                 // Below here: validations
29699                 var isInvalid = false;
29700
29701                 // Check if this connection to `target` could cause relations to break..
29702                 if (target) {
29703                     isInvalid = hasRelationConflict(entity, target, edge, context.graph());
29704                 }
29705
29706                 // Check if this drag causes the geometry to break..
29707                 if (!isInvalid) {
29708                     isInvalid = hasInvalidGeometry(entity, context.graph());
29709                 }
29710
29711
29712                 var nope = context.surface().classed('nope');
29713                 if (isInvalid === 'relation' || isInvalid === 'restriction') {
29714                     if (!nope) {   // about to nope - show hint
29715                         context.ui().flash
29716                             .duration(4000)
29717                             .text(_t('operations.connect.' + isInvalid,
29718                                 { relation: _mainPresetIndex.item('type/restriction').name() }
29719                             ))();
29720                     }
29721                 } else if (isInvalid) {
29722                     var errorID = isInvalid === 'line' ? 'lines' : 'areas';
29723                     context.ui().flash
29724                         .duration(3000)
29725                         .text(_t('self_intersection.error.' + errorID))();
29726                 } else {
29727                     if (nope) {   // about to un-nope, remove hint
29728                         context.ui().flash
29729                             .duration(1)
29730                             .text('')();
29731                     }
29732                 }
29733
29734
29735                 var nopeDisabled = context.surface().classed('nope-disabled');
29736                 if (nopeDisabled) {
29737                     context.surface()
29738                         .classed('nope', false)
29739                         .classed('nope-suppressed', isInvalid);
29740                 } else {
29741                     context.surface()
29742                         .classed('nope', isInvalid)
29743                         .classed('nope-suppressed', false);
29744                 }
29745
29746                 _lastLoc = loc;
29747             }
29748
29749
29750             // Uses `actionConnect.disabled()` to know whether this connection is ok..
29751             function hasRelationConflict(entity, target, edge, graph) {
29752                 var testGraph = graph.update();  // copy
29753
29754                 // if snapping to way - add midpoint there and consider that the target..
29755                 if (edge) {
29756                     var midpoint = osmNode();
29757                     var action = actionAddMidpoint({
29758                         loc: edge.loc,
29759                         edge: [target.nodes[edge.index - 1], target.nodes[edge.index]]
29760                     }, midpoint);
29761
29762                     testGraph = action(testGraph);
29763                     target = midpoint;
29764                 }
29765
29766                 // can we connect to it?
29767                 var ids = [entity.id, target.id];
29768                 return actionConnect(ids).disabled(testGraph);
29769             }
29770
29771
29772             function hasInvalidGeometry(entity, graph) {
29773                 var parents = graph.parentWays(entity);
29774                 var i, j, k;
29775
29776                 for (i = 0; i < parents.length; i++) {
29777                     var parent = parents[i];
29778                     var nodes = [];
29779                     var activeIndex = null;    // which multipolygon ring contains node being dragged
29780
29781                     // test any parent multipolygons for valid geometry
29782                     var relations = graph.parentRelations(parent);
29783                     for (j = 0; j < relations.length; j++) {
29784                         if (!relations[j].isMultipolygon()) { continue; }
29785
29786                         var rings = osmJoinWays(relations[j].members, graph);
29787
29788                         // find active ring and test it for self intersections
29789                         for (k = 0; k < rings.length; k++) {
29790                             nodes = rings[k].nodes;
29791                             if (nodes.find(function(n) { return n.id === entity.id; })) {
29792                                 activeIndex = k;
29793                                 if (geoHasSelfIntersections(nodes, entity.id)) {
29794                                     return 'multipolygonMember';
29795                                 }
29796                             }
29797                             rings[k].coords = nodes.map(function(n) { return n.loc; });
29798                         }
29799
29800                         // test active ring for intersections with other rings in the multipolygon
29801                         for (k = 0; k < rings.length; k++) {
29802                             if (k === activeIndex) { continue; }
29803
29804                             // make sure active ring doesnt cross passive rings
29805                             if (geoHasLineIntersections(rings[activeIndex].nodes, rings[k].nodes, entity.id)) {
29806                                 return 'multipolygonRing';
29807                             }
29808                         }
29809                     }
29810
29811
29812                     // If we still haven't tested this node's parent way for self-intersections.
29813                     // (because it's not a member of a multipolygon), test it now.
29814                     if (activeIndex === null) {
29815                         nodes = parent.nodes.map(function(nodeID) { return graph.entity(nodeID); });
29816                         if (nodes.length && geoHasSelfIntersections(nodes, entity.id)) {
29817                             return parent.geometry(graph);
29818                         }
29819                     }
29820
29821                 }
29822
29823                 return false;
29824             }
29825
29826
29827             function move(entity) {
29828                 if (_isCancelled) { return; }
29829                 event.sourceEvent.stopPropagation();
29830
29831                 context.surface().classed('nope-disabled', event.sourceEvent.altKey);
29832
29833                 _lastLoc = context.projection.invert(event.point);
29834
29835                 doMove(entity);
29836                 var nudge = geoViewportEdge(event.point, context.map().dimensions());
29837                 if (nudge) {
29838                     startNudge(entity, nudge);
29839                 } else {
29840                     stopNudge();
29841                 }
29842             }
29843
29844             function end(entity) {
29845                 if (_isCancelled) { return; }
29846
29847                 var wasPoint = entity.geometry(context.graph()) === 'point';
29848
29849                 var d = datum();
29850                 var nope = (d && d.properties && d.properties.nope) || context.surface().classed('nope');
29851                 var target = d && d.properties && d.properties.entity;   // entity to snap to
29852
29853                 if (nope) {   // bounce back
29854                     context.perform(
29855                         _actionBounceBack(entity.id, _startLoc)
29856                     );
29857
29858                 } else if (target && target.type === 'way') {
29859                     var choice = geoChooseEdge(context.graph().childNodes(target), context.map().mouse(), context.projection, entity.id);
29860                     context.replace(
29861                         actionAddMidpoint({
29862                             loc: choice.loc,
29863                             edge: [target.nodes[choice.index - 1], target.nodes[choice.index]]
29864                         }, entity),
29865                         connectAnnotation(entity, target)
29866                     );
29867
29868                 } else if (target && target.type === 'node' && shouldSnapToNode(target)) {
29869                     context.replace(
29870                         actionConnect([target.id, entity.id]),
29871                         connectAnnotation(entity, target)
29872                     );
29873
29874                 } else if (_wasMidpoint) {
29875                     context.replace(
29876                         actionNoop(),
29877                         _t('operations.add.annotation.vertex')
29878                     );
29879
29880                 } else {
29881                     context.replace(
29882                         actionNoop(),
29883                         moveAnnotation(entity)
29884                     );
29885                 }
29886
29887                 if (wasPoint) {
29888                     context.enter(modeSelect(context, [entity.id]));
29889
29890                 } else {
29891                     var reselection = _restoreSelectedIDs.filter(function(id) {
29892                         return context.graph().hasEntity(id);
29893                     });
29894
29895                     if (reselection.length) {
29896                         context.enter(modeSelect(context, reselection));
29897                     } else {
29898                         context.enter(modeBrowse(context));
29899                     }
29900                 }
29901             }
29902
29903
29904             function _actionBounceBack(nodeID, toLoc) {
29905                 var moveNode = actionMoveNode(nodeID, toLoc);
29906                 var action = function(graph, t) {
29907                     // last time through, pop off the bounceback perform.
29908                     // it will then overwrite the initial perform with a moveNode that does nothing
29909                     if (t === 1) { context.pop(); }
29910                     return moveNode(graph, t);
29911                 };
29912                 action.transitionable = true;
29913                 return action;
29914             }
29915
29916
29917             function cancel() {
29918                 drag.cancel();
29919                 context.enter(modeBrowse(context));
29920             }
29921
29922
29923             var drag = behaviorDrag()
29924                 .selector('.layer-touch.points .target')
29925                 .surface(context.container().select('.main-map').node())
29926                 .origin(origin)
29927                 .on('start', start)
29928                 .on('move', move)
29929                 .on('end', end);
29930
29931
29932             mode.enter = function() {
29933                 context.install(hover);
29934                 context.install(edit);
29935
29936                 select(window)
29937                     .on('keydown.dragNode', keydown)
29938                     .on('keyup.dragNode', keyup);
29939
29940                 context.history()
29941                     .on('undone.drag-node', cancel);
29942             };
29943
29944
29945             mode.exit = function() {
29946                 context.ui().sidebar.hover.cancel();
29947                 context.uninstall(hover);
29948                 context.uninstall(edit);
29949
29950                 select(window)
29951                     .on('keydown.dragNode', null)
29952                     .on('keyup.dragNode', null);
29953
29954                 context.history()
29955                     .on('undone.drag-node', null);
29956
29957                 _activeEntity = null;
29958
29959                 context.surface()
29960                     .classed('nope', false)
29961                     .classed('nope-suppressed', false)
29962                     .classed('nope-disabled', false)
29963                     .selectAll('.active')
29964                     .classed('active', false);
29965
29966                 stopNudge();
29967             };
29968
29969
29970             mode.selectedIDs = function() {
29971                 if (!arguments.length) { return _activeEntity ? [_activeEntity.id] : []; }
29972                 // no assign
29973                 return mode;
29974             };
29975
29976
29977             mode.activeID = function() {
29978                 if (!arguments.length) { return _activeEntity && _activeEntity.id; }
29979                 // no assign
29980                 return mode;
29981             };
29982
29983
29984             mode.restoreSelectedIDs = function(_) {
29985                 if (!arguments.length) { return _restoreSelectedIDs; }
29986                 _restoreSelectedIDs = _;
29987                 return mode;
29988             };
29989
29990
29991             mode.behavior = drag;
29992
29993
29994             return mode;
29995         }
29996
29997         function quickselect(arr, k, left, right, compare) {
29998             quickselectStep(arr, k, left || 0, right || (arr.length - 1), compare || defaultCompare);
29999         }
30000
30001         function quickselectStep(arr, k, left, right, compare) {
30002
30003             while (right > left) {
30004                 if (right - left > 600) {
30005                     var n = right - left + 1;
30006                     var m = k - left + 1;
30007                     var z = Math.log(n);
30008                     var s = 0.5 * Math.exp(2 * z / 3);
30009                     var sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1);
30010                     var newLeft = Math.max(left, Math.floor(k - m * s / n + sd));
30011                     var newRight = Math.min(right, Math.floor(k + (n - m) * s / n + sd));
30012                     quickselectStep(arr, k, newLeft, newRight, compare);
30013                 }
30014
30015                 var t = arr[k];
30016                 var i = left;
30017                 var j = right;
30018
30019                 swap(arr, left, k);
30020                 if (compare(arr[right], t) > 0) { swap(arr, left, right); }
30021
30022                 while (i < j) {
30023                     swap(arr, i, j);
30024                     i++;
30025                     j--;
30026                     while (compare(arr[i], t) < 0) { i++; }
30027                     while (compare(arr[j], t) > 0) { j--; }
30028                 }
30029
30030                 if (compare(arr[left], t) === 0) { swap(arr, left, j); }
30031                 else {
30032                     j++;
30033                     swap(arr, j, right);
30034                 }
30035
30036                 if (j <= k) { left = j + 1; }
30037                 if (k <= j) { right = j - 1; }
30038             }
30039         }
30040
30041         function swap(arr, i, j) {
30042             var tmp = arr[i];
30043             arr[i] = arr[j];
30044             arr[j] = tmp;
30045         }
30046
30047         function defaultCompare(a, b) {
30048             return a < b ? -1 : a > b ? 1 : 0;
30049         }
30050
30051         var RBush = function RBush(maxEntries) {
30052             if ( maxEntries === void 0 ) maxEntries = 9;
30053
30054             // max entries in a node is 9 by default; min node fill is 40% for best performance
30055             this._maxEntries = Math.max(4, maxEntries);
30056             this._minEntries = Math.max(2, Math.ceil(this._maxEntries * 0.4));
30057             this.clear();
30058         };
30059
30060         RBush.prototype.all = function all () {
30061             return this._all(this.data, []);
30062         };
30063
30064         RBush.prototype.search = function search (bbox) {
30065             var node = this.data;
30066             var result = [];
30067
30068             if (!intersects(bbox, node)) { return result; }
30069
30070             var toBBox = this.toBBox;
30071             var nodesToSearch = [];
30072
30073             while (node) {
30074                 for (var i = 0; i < node.children.length; i++) {
30075                     var child = node.children[i];
30076                     var childBBox = node.leaf ? toBBox(child) : child;
30077
30078                     if (intersects(bbox, childBBox)) {
30079                         if (node.leaf) { result.push(child); }
30080                         else if (contains$1(bbox, childBBox)) { this._all(child, result); }
30081                         else { nodesToSearch.push(child); }
30082                     }
30083                 }
30084                 node = nodesToSearch.pop();
30085             }
30086
30087             return result;
30088         };
30089
30090         RBush.prototype.collides = function collides (bbox) {
30091             var node = this.data;
30092
30093             if (!intersects(bbox, node)) { return false; }
30094
30095             var nodesToSearch = [];
30096             while (node) {
30097                 for (var i = 0; i < node.children.length; i++) {
30098                     var child = node.children[i];
30099                     var childBBox = node.leaf ? this.toBBox(child) : child;
30100
30101                     if (intersects(bbox, childBBox)) {
30102                         if (node.leaf || contains$1(bbox, childBBox)) { return true; }
30103                         nodesToSearch.push(child);
30104                     }
30105                 }
30106                 node = nodesToSearch.pop();
30107             }
30108
30109             return false;
30110         };
30111
30112         RBush.prototype.load = function load (data) {
30113             if (!(data && data.length)) { return this; }
30114
30115             if (data.length < this._minEntries) {
30116                 for (var i = 0; i < data.length; i++) {
30117                     this.insert(data[i]);
30118                 }
30119                 return this;
30120             }
30121
30122             // recursively build the tree with the given data from scratch using OMT algorithm
30123             var node = this._build(data.slice(), 0, data.length - 1, 0);
30124
30125             if (!this.data.children.length) {
30126                 // save as is if tree is empty
30127                 this.data = node;
30128
30129             } else if (this.data.height === node.height) {
30130                 // split root if trees have the same height
30131                 this._splitRoot(this.data, node);
30132
30133             } else {
30134                 if (this.data.height < node.height) {
30135                     // swap trees if inserted one is bigger
30136                     var tmpNode = this.data;
30137                     this.data = node;
30138                     node = tmpNode;
30139                 }
30140
30141                 // insert the small tree into the large tree at appropriate level
30142                 this._insert(node, this.data.height - node.height - 1, true);
30143             }
30144
30145             return this;
30146         };
30147
30148         RBush.prototype.insert = function insert (item) {
30149             if (item) { this._insert(item, this.data.height - 1); }
30150             return this;
30151         };
30152
30153         RBush.prototype.clear = function clear () {
30154             this.data = createNode([]);
30155             return this;
30156         };
30157
30158         RBush.prototype.remove = function remove (item, equalsFn) {
30159             if (!item) { return this; }
30160
30161             var node = this.data;
30162             var bbox = this.toBBox(item);
30163             var path = [];
30164             var indexes = [];
30165             var i, parent, goingUp;
30166
30167             // depth-first iterative tree traversal
30168             while (node || path.length) {
30169
30170                 if (!node) { // go up
30171                     node = path.pop();
30172                     parent = path[path.length - 1];
30173                     i = indexes.pop();
30174                     goingUp = true;
30175                 }
30176
30177                 if (node.leaf) { // check current node
30178                     var index = findItem(item, node.children, equalsFn);
30179
30180                     if (index !== -1) {
30181                         // item found, remove the item and condense tree upwards
30182                         node.children.splice(index, 1);
30183                         path.push(node);
30184                         this._condense(path);
30185                         return this;
30186                     }
30187                 }
30188
30189                 if (!goingUp && !node.leaf && contains$1(node, bbox)) { // go down
30190                     path.push(node);
30191                     indexes.push(i);
30192                     i = 0;
30193                     parent = node;
30194                     node = node.children[0];
30195
30196                 } else if (parent) { // go right
30197                     i++;
30198                     node = parent.children[i];
30199                     goingUp = false;
30200
30201                 } else { node = null; } // nothing found
30202             }
30203
30204             return this;
30205         };
30206
30207         RBush.prototype.toBBox = function toBBox (item) { return item; };
30208
30209         RBush.prototype.compareMinX = function compareMinX (a, b) { return a.minX - b.minX; };
30210         RBush.prototype.compareMinY = function compareMinY (a, b) { return a.minY - b.minY; };
30211
30212         RBush.prototype.toJSON = function toJSON () { return this.data; };
30213
30214         RBush.prototype.fromJSON = function fromJSON (data) {
30215             this.data = data;
30216             return this;
30217         };
30218
30219         RBush.prototype._all = function _all (node, result) {
30220             var nodesToSearch = [];
30221             while (node) {
30222                 if (node.leaf) { result.push.apply(result, node.children); }
30223                 else { nodesToSearch.push.apply(nodesToSearch, node.children); }
30224
30225                 node = nodesToSearch.pop();
30226             }
30227             return result;
30228         };
30229
30230         RBush.prototype._build = function _build (items, left, right, height) {
30231
30232             var N = right - left + 1;
30233             var M = this._maxEntries;
30234             var node;
30235
30236             if (N <= M) {
30237                 // reached leaf level; return leaf
30238                 node = createNode(items.slice(left, right + 1));
30239                 calcBBox(node, this.toBBox);
30240                 return node;
30241             }
30242
30243             if (!height) {
30244                 // target height of the bulk-loaded tree
30245                 height = Math.ceil(Math.log(N) / Math.log(M));
30246
30247                 // target number of root entries to maximize storage utilization
30248                 M = Math.ceil(N / Math.pow(M, height - 1));
30249             }
30250
30251             node = createNode([]);
30252             node.leaf = false;
30253             node.height = height;
30254
30255             // split the items into M mostly square tiles
30256
30257             var N2 = Math.ceil(N / M);
30258             var N1 = N2 * Math.ceil(Math.sqrt(M));
30259
30260             multiSelect(items, left, right, N1, this.compareMinX);
30261
30262             for (var i = left; i <= right; i += N1) {
30263
30264                 var right2 = Math.min(i + N1 - 1, right);
30265
30266                 multiSelect(items, i, right2, N2, this.compareMinY);
30267
30268                 for (var j = i; j <= right2; j += N2) {
30269
30270                     var right3 = Math.min(j + N2 - 1, right2);
30271
30272                     // pack each entry recursively
30273                     node.children.push(this._build(items, j, right3, height - 1));
30274                 }
30275             }
30276
30277             calcBBox(node, this.toBBox);
30278
30279             return node;
30280         };
30281
30282         RBush.prototype._chooseSubtree = function _chooseSubtree (bbox, node, level, path) {
30283             while (true) {
30284                 path.push(node);
30285
30286                 if (node.leaf || path.length - 1 === level) { break; }
30287
30288                 var minArea = Infinity;
30289                 var minEnlargement = Infinity;
30290                 var targetNode = (void 0);
30291
30292                 for (var i = 0; i < node.children.length; i++) {
30293                     var child = node.children[i];
30294                     var area = bboxArea(child);
30295                     var enlargement = enlargedArea(bbox, child) - area;
30296
30297                     // choose entry with the least area enlargement
30298                     if (enlargement < minEnlargement) {
30299                         minEnlargement = enlargement;
30300                         minArea = area < minArea ? area : minArea;
30301                         targetNode = child;
30302
30303                     } else if (enlargement === minEnlargement) {
30304                         // otherwise choose one with the smallest area
30305                         if (area < minArea) {
30306                             minArea = area;
30307                             targetNode = child;
30308                         }
30309                     }
30310                 }
30311
30312                 node = targetNode || node.children[0];
30313             }
30314
30315             return node;
30316         };
30317
30318         RBush.prototype._insert = function _insert (item, level, isNode) {
30319             var bbox = isNode ? item : this.toBBox(item);
30320             var insertPath = [];
30321
30322             // find the best node for accommodating the item, saving all nodes along the path too
30323             var node = this._chooseSubtree(bbox, this.data, level, insertPath);
30324
30325             // put the item into the node
30326             node.children.push(item);
30327             extend$1(node, bbox);
30328
30329             // split on node overflow; propagate upwards if necessary
30330             while (level >= 0) {
30331                 if (insertPath[level].children.length > this._maxEntries) {
30332                     this._split(insertPath, level);
30333                     level--;
30334                 } else { break; }
30335             }
30336
30337             // adjust bboxes along the insertion path
30338             this._adjustParentBBoxes(bbox, insertPath, level);
30339         };
30340
30341         // split overflowed node into two
30342         RBush.prototype._split = function _split (insertPath, level) {
30343             var node = insertPath[level];
30344             var M = node.children.length;
30345             var m = this._minEntries;
30346
30347             this._chooseSplitAxis(node, m, M);
30348
30349             var splitIndex = this._chooseSplitIndex(node, m, M);
30350
30351             var newNode = createNode(node.children.splice(splitIndex, node.children.length - splitIndex));
30352             newNode.height = node.height;
30353             newNode.leaf = node.leaf;
30354
30355             calcBBox(node, this.toBBox);
30356             calcBBox(newNode, this.toBBox);
30357
30358             if (level) { insertPath[level - 1].children.push(newNode); }
30359             else { this._splitRoot(node, newNode); }
30360         };
30361
30362         RBush.prototype._splitRoot = function _splitRoot (node, newNode) {
30363             // split root node
30364             this.data = createNode([node, newNode]);
30365             this.data.height = node.height + 1;
30366             this.data.leaf = false;
30367             calcBBox(this.data, this.toBBox);
30368         };
30369
30370         RBush.prototype._chooseSplitIndex = function _chooseSplitIndex (node, m, M) {
30371             var index;
30372             var minOverlap = Infinity;
30373             var minArea = Infinity;
30374
30375             for (var i = m; i <= M - m; i++) {
30376                 var bbox1 = distBBox(node, 0, i, this.toBBox);
30377                 var bbox2 = distBBox(node, i, M, this.toBBox);
30378
30379                 var overlap = intersectionArea(bbox1, bbox2);
30380                 var area = bboxArea(bbox1) + bboxArea(bbox2);
30381
30382                 // choose distribution with minimum overlap
30383                 if (overlap < minOverlap) {
30384                     minOverlap = overlap;
30385                     index = i;
30386
30387                     minArea = area < minArea ? area : minArea;
30388
30389                 } else if (overlap === minOverlap) {
30390                     // otherwise choose distribution with minimum area
30391                     if (area < minArea) {
30392                         minArea = area;
30393                         index = i;
30394                     }
30395                 }
30396             }
30397
30398             return index || M - m;
30399         };
30400
30401         // sorts node children by the best axis for split
30402         RBush.prototype._chooseSplitAxis = function _chooseSplitAxis (node, m, M) {
30403             var compareMinX = node.leaf ? this.compareMinX : compareNodeMinX;
30404             var compareMinY = node.leaf ? this.compareMinY : compareNodeMinY;
30405             var xMargin = this._allDistMargin(node, m, M, compareMinX);
30406             var yMargin = this._allDistMargin(node, m, M, compareMinY);
30407
30408             // if total distributions margin value is minimal for x, sort by minX,
30409             // otherwise it's already sorted by minY
30410             if (xMargin < yMargin) { node.children.sort(compareMinX); }
30411         };
30412
30413         // total margin of all possible split distributions where each node is at least m full
30414         RBush.prototype._allDistMargin = function _allDistMargin (node, m, M, compare) {
30415             node.children.sort(compare);
30416
30417             var toBBox = this.toBBox;
30418             var leftBBox = distBBox(node, 0, m, toBBox);
30419             var rightBBox = distBBox(node, M - m, M, toBBox);
30420             var margin = bboxMargin(leftBBox) + bboxMargin(rightBBox);
30421
30422             for (var i = m; i < M - m; i++) {
30423                 var child = node.children[i];
30424                 extend$1(leftBBox, node.leaf ? toBBox(child) : child);
30425                 margin += bboxMargin(leftBBox);
30426             }
30427
30428             for (var i$1 = M - m - 1; i$1 >= m; i$1--) {
30429                 var child$1 = node.children[i$1];
30430                 extend$1(rightBBox, node.leaf ? toBBox(child$1) : child$1);
30431                 margin += bboxMargin(rightBBox);
30432             }
30433
30434             return margin;
30435         };
30436
30437         RBush.prototype._adjustParentBBoxes = function _adjustParentBBoxes (bbox, path, level) {
30438             // adjust bboxes along the given tree path
30439             for (var i = level; i >= 0; i--) {
30440                 extend$1(path[i], bbox);
30441             }
30442         };
30443
30444         RBush.prototype._condense = function _condense (path) {
30445             // go through the path, removing empty nodes and updating bboxes
30446             for (var i = path.length - 1, siblings = (void 0); i >= 0; i--) {
30447                 if (path[i].children.length === 0) {
30448                     if (i > 0) {
30449                         siblings = path[i - 1].children;
30450                         siblings.splice(siblings.indexOf(path[i]), 1);
30451
30452                     } else { this.clear(); }
30453
30454                 } else { calcBBox(path[i], this.toBBox); }
30455             }
30456         };
30457
30458         function findItem(item, items, equalsFn) {
30459             if (!equalsFn) { return items.indexOf(item); }
30460
30461             for (var i = 0; i < items.length; i++) {
30462                 if (equalsFn(item, items[i])) { return i; }
30463             }
30464             return -1;
30465         }
30466
30467         // calculate node's bbox from bboxes of its children
30468         function calcBBox(node, toBBox) {
30469             distBBox(node, 0, node.children.length, toBBox, node);
30470         }
30471
30472         // min bounding rectangle of node children from k to p-1
30473         function distBBox(node, k, p, toBBox, destNode) {
30474             if (!destNode) { destNode = createNode(null); }
30475             destNode.minX = Infinity;
30476             destNode.minY = Infinity;
30477             destNode.maxX = -Infinity;
30478             destNode.maxY = -Infinity;
30479
30480             for (var i = k; i < p; i++) {
30481                 var child = node.children[i];
30482                 extend$1(destNode, node.leaf ? toBBox(child) : child);
30483             }
30484
30485             return destNode;
30486         }
30487
30488         function extend$1(a, b) {
30489             a.minX = Math.min(a.minX, b.minX);
30490             a.minY = Math.min(a.minY, b.minY);
30491             a.maxX = Math.max(a.maxX, b.maxX);
30492             a.maxY = Math.max(a.maxY, b.maxY);
30493             return a;
30494         }
30495
30496         function compareNodeMinX(a, b) { return a.minX - b.minX; }
30497         function compareNodeMinY(a, b) { return a.minY - b.minY; }
30498
30499         function bboxArea(a)   { return (a.maxX - a.minX) * (a.maxY - a.minY); }
30500         function bboxMargin(a) { return (a.maxX - a.minX) + (a.maxY - a.minY); }
30501
30502         function enlargedArea(a, b) {
30503             return (Math.max(b.maxX, a.maxX) - Math.min(b.minX, a.minX)) *
30504                    (Math.max(b.maxY, a.maxY) - Math.min(b.minY, a.minY));
30505         }
30506
30507         function intersectionArea(a, b) {
30508             var minX = Math.max(a.minX, b.minX);
30509             var minY = Math.max(a.minY, b.minY);
30510             var maxX = Math.min(a.maxX, b.maxX);
30511             var maxY = Math.min(a.maxY, b.maxY);
30512
30513             return Math.max(0, maxX - minX) *
30514                    Math.max(0, maxY - minY);
30515         }
30516
30517         function contains$1(a, b) {
30518             return a.minX <= b.minX &&
30519                    a.minY <= b.minY &&
30520                    b.maxX <= a.maxX &&
30521                    b.maxY <= a.maxY;
30522         }
30523
30524         function intersects(a, b) {
30525             return b.minX <= a.maxX &&
30526                    b.minY <= a.maxY &&
30527                    b.maxX >= a.minX &&
30528                    b.maxY >= a.minY;
30529         }
30530
30531         function createNode(children) {
30532             return {
30533                 children: children,
30534                 height: 1,
30535                 leaf: true,
30536                 minX: Infinity,
30537                 minY: Infinity,
30538                 maxX: -Infinity,
30539                 maxY: -Infinity
30540             };
30541         }
30542
30543         // sort an array so that items come in groups of n unsorted items, with groups sorted between each other;
30544         // combines selection algorithm with binary divide & conquer approach
30545
30546         function multiSelect(arr, left, right, n, compare) {
30547             var stack = [left, right];
30548
30549             while (stack.length) {
30550                 right = stack.pop();
30551                 left = stack.pop();
30552
30553                 if (right - left <= n) { continue; }
30554
30555                 var mid = left + Math.ceil((right - left) / n / 2) * n;
30556                 quickselect(arr, mid, left, right, compare);
30557
30558                 stack.push(left, mid, mid, right);
30559             }
30560         }
30561
30562         var tiler = utilTiler();
30563         var dispatch$1 = dispatch('loaded');
30564         var _tileZoom = 14;
30565         var _krUrlRoot = 'https://www.keepright.at';
30566         var _krData = { errorTypes: {}, localizeStrings: {} };
30567
30568         // This gets reassigned if reset
30569         var _cache;
30570
30571         var _krRuleset = [
30572           // no 20 - multiple node on same spot - these are mostly boundaries overlapping roads
30573           30, 40, 50, 60, 70, 90, 100, 110, 120, 130, 150, 160, 170, 180,
30574           190, 191, 192, 193, 194, 195, 196, 197, 198,
30575           200, 201, 202, 203, 204, 205, 206, 207, 208, 210, 220,
30576           230, 231, 232, 270, 280, 281, 282, 283, 284, 285,
30577           290, 291, 292, 293, 294, 295, 296, 297, 298, 300, 310, 311, 312, 313,
30578           320, 350, 360, 370, 380, 390, 400, 401, 402, 410, 411, 412, 413
30579         ];
30580
30581
30582         function abortRequest(controller) {
30583           if (controller) {
30584             controller.abort();
30585           }
30586         }
30587
30588         function abortUnwantedRequests(cache, tiles) {
30589           Object.keys(cache.inflightTile).forEach(function (k) {
30590             var wanted = tiles.find(function (tile) { return k === tile.id; });
30591             if (!wanted) {
30592               abortRequest(cache.inflightTile[k]);
30593               delete cache.inflightTile[k];
30594             }
30595           });
30596         }
30597
30598
30599         function encodeIssueRtree(d) {
30600           return { minX: d.loc[0], minY: d.loc[1], maxX: d.loc[0], maxY: d.loc[1], data: d };
30601         }
30602
30603
30604         // Replace or remove QAItem from rtree
30605         function updateRtree(item, replace) {
30606           _cache.rtree.remove(item, function (a, b) { return a.data.id === b.data.id; });
30607
30608           if (replace) {
30609             _cache.rtree.insert(item);
30610           }
30611         }
30612
30613
30614         function tokenReplacements(d) {
30615           if (!(d instanceof QAItem)) { return; }
30616
30617           var htmlRegex = new RegExp(/<\/[a-z][\s\S]*>/);
30618           var replacements = {};
30619
30620           var issueTemplate = _krData.errorTypes[d.whichType];
30621           if (!issueTemplate) {
30622             /* eslint-disable no-console */
30623             console.log('No Template: ', d.whichType);
30624             console.log('  ', d.description);
30625             /* eslint-enable no-console */
30626             return;
30627           }
30628
30629           // some descriptions are just fixed text
30630           if (!issueTemplate.regex) { return; }
30631
30632           // regex pattern should match description with variable details captured
30633           var errorRegex = new RegExp(issueTemplate.regex, 'i');
30634           var errorMatch = errorRegex.exec(d.description);
30635           if (!errorMatch) {
30636             /* eslint-disable no-console */
30637             console.log('Unmatched: ', d.whichType);
30638             console.log('  ', d.description);
30639             console.log('  ', errorRegex);
30640             /* eslint-enable no-console */
30641             return;
30642           }
30643
30644           for (var i = 1; i < errorMatch.length; i++) {   // skip first
30645             var capture = errorMatch[i];
30646             var idType = (void 0);
30647
30648             idType = 'IDs' in issueTemplate ? issueTemplate.IDs[i-1] : '';
30649             if (idType && capture) {   // link IDs if present in the capture
30650               capture = parseError(capture, idType);
30651             } else if (htmlRegex.test(capture)) {   // escape any html in non-IDs
30652               capture = '\\' +  capture + '\\';
30653             } else {
30654               var compare = capture.toLowerCase();
30655               if (_krData.localizeStrings[compare]) {   // some replacement strings can be localized
30656                 capture = _t('QA.keepRight.error_parts.' + _krData.localizeStrings[compare]);
30657               }
30658             }
30659
30660             replacements['var' + i] = capture;
30661           }
30662
30663           return replacements;
30664         }
30665
30666
30667         function parseError(capture, idType) {
30668           var compare = capture.toLowerCase();
30669           if (_krData.localizeStrings[compare]) {   // some replacement strings can be localized
30670             capture = _t('QA.keepRight.error_parts.' + _krData.localizeStrings[compare]);
30671           }
30672
30673           switch (idType) {
30674             // link a string like "this node"
30675             case 'this':
30676               capture = linkErrorObject(capture);
30677               break;
30678
30679             case 'url':
30680               capture = linkURL(capture);
30681               break;
30682
30683             // link an entity ID
30684             case 'n':
30685             case 'w':
30686             case 'r':
30687               capture = linkEntity(idType + capture);
30688               break;
30689
30690             // some errors have more complex ID lists/variance
30691             case '20':
30692               capture = parse20(capture);
30693               break;
30694             case '211':
30695               capture = parse211(capture);
30696               break;
30697             case '231':
30698               capture = parse231(capture);
30699               break;
30700             case '294':
30701               capture = parse294(capture);
30702               break;
30703             case '370':
30704               capture = parse370(capture);
30705               break;
30706           }
30707
30708           return capture;
30709
30710
30711           function linkErrorObject(d) {
30712             return ("<a class=\"error_object_link\">" + d + "</a>");
30713           }
30714
30715           function linkEntity(d) {
30716             return ("<a class=\"error_entity_link\">" + d + "</a>");
30717           }
30718
30719           function linkURL(d) {
30720             return ("<a class=\"kr_external_link\" target=\"_blank\" href=\"" + d + "\">" + d + "</a>");
30721           }
30722
30723           // arbitrary node list of form: #ID, #ID, #ID...
30724           function parse211(capture) {
30725             var newList = [];
30726             var items = capture.split(', ');
30727
30728             items.forEach(function (item) {
30729               // ID has # at the front
30730               var id = linkEntity('n' + item.slice(1));
30731               newList.push(id);
30732             });
30733
30734             return newList.join(', ');
30735           }
30736
30737           // arbitrary way list of form: #ID(layer),#ID(layer),#ID(layer)...
30738           function parse231(capture) {
30739             var newList = [];
30740             // unfortunately 'layer' can itself contain commas, so we split on '),'
30741             var items = capture.split('),');
30742
30743             items.forEach(function (item) {
30744               var match = item.match(/\#(\d+)\((.+)\)?/);
30745               if (match !== null && match.length > 2) {
30746                 newList.push(linkEntity('w' + match[1]) + ' ' +
30747                   _t('QA.keepRight.errorTypes.231.layer', { layer: match[2] })
30748                 );
30749               }
30750             });
30751
30752             return newList.join(', ');
30753           }
30754
30755           // arbitrary node/relation list of form: from node #ID,to relation #ID,to node #ID...
30756           function parse294(capture) {
30757             var newList = [];
30758             var items = capture.split(',');
30759
30760             items.forEach(function (item) {
30761               // item of form "from/to node/relation #ID"
30762               item = item.split(' ');
30763
30764               // to/from role is more clear in quotes
30765               var role = "\"" + (item[0]) + "\"";
30766
30767               // first letter of node/relation provides the type
30768               var idType = item[1].slice(0,1);
30769
30770               // ID has # at the front
30771               var id = item[2].slice(1);
30772               id = linkEntity(idType + id);
30773
30774               newList.push((role + " " + (item[1]) + " " + id));
30775             });
30776
30777             return newList.join(', ');
30778           }
30779
30780           // may or may not include the string "(including the name 'name')"
30781           function parse370(capture) {
30782             if (!capture) { return ''; }
30783
30784             var match = capture.match(/\(including the name (\'.+\')\)/);
30785             if (match && match.length) {
30786               return _t('QA.keepRight.errorTypes.370.including_the_name', { name: match[1] });
30787             }
30788             return '';
30789           }
30790
30791           // arbitrary node list of form: #ID,#ID,#ID...
30792           function parse20(capture) {
30793             var newList = [];
30794             var items = capture.split(',');
30795
30796             items.forEach(function (item) {
30797               // ID has # at the front
30798               var id = linkEntity('n' + item.slice(1));
30799               newList.push(id);
30800             });
30801
30802             return newList.join(', ');
30803           }
30804         }
30805
30806
30807         var serviceKeepRight = {
30808           title: 'keepRight',
30809
30810           init: function init() {
30811             _mainFileFetcher.get('keepRight')
30812               .then(function (d) { return _krData = d; });
30813
30814             if (!_cache) {
30815               this.reset();
30816             }
30817
30818             this.event = utilRebind(this, dispatch$1, 'on');
30819           },
30820
30821           reset: function reset() {
30822             if (_cache) {
30823               Object.values(_cache.inflightTile).forEach(abortRequest);
30824             }
30825
30826             _cache = {
30827               data: {},
30828               loadedTile: {},
30829               inflightTile: {},
30830               inflightPost: {},
30831               closed: {},
30832               rtree: new RBush()
30833             };
30834           },
30835
30836
30837           // KeepRight API:  http://osm.mueschelsoft.de/keepright/interfacing.php
30838           loadIssues: function loadIssues(projection) {
30839             var this$1 = this;
30840
30841             var options = {
30842               format: 'geojson',
30843               ch: _krRuleset
30844             };
30845
30846             // determine the needed tiles to cover the view
30847             var tiles = tiler
30848               .zoomExtent([_tileZoom, _tileZoom])
30849               .getTiles(projection);
30850
30851             // abort inflight requests that are no longer needed
30852             abortUnwantedRequests(_cache, tiles);
30853
30854             // issue new requests..
30855             tiles.forEach(function (tile) {
30856               if (_cache.loadedTile[tile.id] || _cache.inflightTile[tile.id]) { return; }
30857
30858               var ref = tile.extent.rectangle();
30859               var left = ref[0];
30860               var top = ref[1];
30861               var right = ref[2];
30862               var bottom = ref[3];
30863               var params = Object.assign({}, options, { left: left, bottom: bottom, right: right, top: top });
30864               var url = _krUrlRoot + "/export.php?" + utilQsString(params);
30865               var controller = new AbortController();
30866
30867               _cache.inflightTile[tile.id] = controller;
30868
30869               d3_json(url, { signal: controller.signal })
30870                 .then(function (data) {
30871                   delete _cache.inflightTile[tile.id];
30872                   _cache.loadedTile[tile.id] = true;
30873                   if (!data || !data.features || !data.features.length) {
30874                     throw new Error('No Data');
30875                   }
30876
30877                   data.features.forEach(function (feature) {
30878                     var feature_properties = feature.properties;
30879                     var itemType = feature_properties.error_type;
30880                     var id = feature_properties.error_id;
30881                     var comment = feature_properties.comment; if ( comment === void 0 ) comment = null;
30882                     var objectId = feature_properties.object_id;
30883                     var objectType = feature_properties.object_type;
30884                     var schema = feature_properties.schema;
30885                     var title = feature_properties.title;
30886                     var loc = feature.geometry.coordinates;
30887                     var description = feature.properties.description; if ( description === void 0 ) description = '';
30888
30889                     // if there is a parent, save its error type e.g.:
30890                     //  Error 191 = "highway-highway"
30891                     //  Error 190 = "intersections without junctions"  (parent)
30892                     var issueTemplate = _krData.errorTypes[itemType];
30893                     var parentIssueType = (Math.floor(itemType / 10) * 10).toString();
30894
30895                     // try to handle error type directly, fallback to parent error type.
30896                     var whichType = issueTemplate ? itemType : parentIssueType;
30897                     var whichTemplate = _krData.errorTypes[whichType];
30898
30899                     // Rewrite a few of the errors at this point..
30900                     // This is done to make them easier to linkify and translate.
30901                     switch (whichType) {
30902                       case '170':
30903                         description = "This feature has a FIXME tag: " + description;
30904                         break;
30905                       case '292':
30906                       case '293':
30907                         description = description.replace('A turn-', 'This turn-');
30908                         break;
30909                       case '294':
30910                       case '295':
30911                       case '296':
30912                       case '297':
30913                       case '298':
30914                         description = "This turn-restriction~" + description;
30915                         break;
30916                       case '300':
30917                         description = 'This highway is missing a maxspeed tag';
30918                         break;
30919                       case '411':
30920                       case '412':
30921                       case '413':
30922                         description = "This feature~" + description;
30923                         break;
30924                     }
30925
30926                     // move markers slightly so it doesn't obscure the geometry,
30927                     // then move markers away from other coincident markers
30928                     var coincident = false;
30929                     do {
30930                       // first time, move marker up. after that, move marker right.
30931                       var delta = coincident ? [0.00001, 0] : [0, 0.00001];
30932                       loc = geoVecAdd(loc, delta);
30933                       var bbox = geoExtent(loc).bbox();
30934                       coincident = _cache.rtree.search(bbox).length;
30935                     } while (coincident);
30936
30937                     var d = new QAItem(loc, this$1, itemType, id, {
30938                       comment: comment,
30939                       description: description,
30940                       whichType: whichType,
30941                       parentIssueType: parentIssueType,
30942                       severity: whichTemplate.severity || 'error',
30943                       objectId: objectId,
30944                       objectType: objectType,
30945                       schema: schema,
30946                       title: title
30947                     });
30948
30949                     d.replacements = tokenReplacements(d);
30950
30951                     _cache.data[id] = d;
30952                     _cache.rtree.insert(encodeIssueRtree(d));
30953                   });
30954
30955                   dispatch$1.call('loaded');
30956                 })
30957                 .catch(function () {
30958                   delete _cache.inflightTile[tile.id];
30959                   _cache.loadedTile[tile.id] = true;
30960                 });
30961
30962             });
30963           },
30964
30965
30966           postUpdate: function postUpdate(d, callback) {
30967             var this$1 = this;
30968
30969             if (_cache.inflightPost[d.id]) {
30970               return callback({ message: 'Error update already inflight', status: -2 }, d);
30971             }
30972
30973             var params = { schema: d.schema, id: d.id };
30974
30975             if (d.newStatus) {
30976               params.st = d.newStatus;
30977             }
30978             if (d.newComment !== undefined) {
30979               params.co = d.newComment;
30980             }
30981
30982             // NOTE: This throws a CORS err, but it seems successful.
30983             // We don't care too much about the response, so this is fine.
30984             var url = _krUrlRoot + "/comment.php?" + utilQsString(params);
30985             var controller = new AbortController();
30986
30987             _cache.inflightPost[d.id] = controller;
30988
30989             // Since this is expected to throw an error just continue as if it worked
30990             // (worst case scenario the request truly fails and issue will show up if iD restarts)
30991             d3_json(url, { signal: controller.signal })
30992               .finally(function () {
30993                 delete _cache.inflightPost[d.id];
30994
30995                 if (d.newStatus === 'ignore') {
30996                   // ignore permanently (false positive)
30997                   this$1.removeItem(d);
30998                 } else if (d.newStatus === 'ignore_t') {
30999                   // ignore temporarily (error fixed)
31000                   this$1.removeItem(d);
31001                   _cache.closed[((d.schema) + ":" + (d.id))] = true;
31002                 } else {
31003                   d = this$1.replaceItem(d.update({
31004                     comment: d.newComment,
31005                     newComment: undefined,
31006                     newState: undefined
31007                   }));
31008                 }
31009
31010                 if (callback) { callback(null, d); }
31011               });
31012           },
31013
31014           // Get all cached QAItems covering the viewport
31015           getItems: function getItems(projection) {
31016             var viewport = projection.clipExtent();
31017             var min = [viewport[0][0], viewport[1][1]];
31018             var max = [viewport[1][0], viewport[0][1]];
31019             var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
31020
31021             return _cache.rtree.search(bbox).map(function (d) { return d.data; });
31022           },
31023
31024           // Get a QAItem from cache
31025           // NOTE: Don't change method name until UI v3 is merged
31026           getError: function getError(id) {
31027             return _cache.data[id];
31028           },
31029
31030           // Replace a single QAItem in the cache
31031           replaceItem: function replaceItem(item) {
31032             if (!(item instanceof QAItem) || !item.id) { return; }
31033
31034             _cache.data[item.id] = item;
31035             updateRtree(encodeIssueRtree(item), true); // true = replace
31036             return item;
31037           },
31038
31039           // Remove a single QAItem from the cache
31040           removeItem: function removeItem(item) {
31041             if (!(item instanceof QAItem) || !item.id) { return; }
31042
31043             delete _cache.data[item.id];
31044             updateRtree(encodeIssueRtree(item), false); // false = remove
31045           },
31046
31047           issueURL: function issueURL(item) {
31048             return (_krUrlRoot + "/report_map.php?schema=" + (item.schema) + "&error=" + (item.id));
31049           },
31050
31051           // Get an array of issues closed during this session.
31052           // Used to populate `closed:keepright` changeset tag
31053           getClosedIDs: function getClosedIDs() {
31054             return Object.keys(_cache.closed).sort();
31055           }
31056
31057         };
31058
31059         var tiler$1 = utilTiler();
31060         var dispatch$2 = dispatch('loaded');
31061         var _tileZoom$1 = 14;
31062         var _impOsmUrls = {
31063           ow: 'https://grab.community.improve-osm.org/directionOfFlowService',
31064           mr: 'https://grab.community.improve-osm.org/missingGeoService',
31065           tr: 'https://grab.community.improve-osm.org/turnRestrictionService'
31066         };
31067         var _impOsmData = { icons: {} };
31068
31069
31070         // This gets reassigned if reset
31071         var _cache$1;
31072
31073         function abortRequest$1(i) {
31074           Object.values(i).forEach(function (controller) {
31075             if (controller) {
31076               controller.abort();
31077             }
31078           });
31079         }
31080
31081         function abortUnwantedRequests$1(cache, tiles) {
31082           Object.keys(cache.inflightTile).forEach(function (k) {
31083             var wanted = tiles.find(function (tile) { return k === tile.id; });
31084             if (!wanted) {
31085               abortRequest$1(cache.inflightTile[k]);
31086               delete cache.inflightTile[k];
31087             }
31088           });
31089         }
31090
31091         function encodeIssueRtree$1(d) {
31092           return { minX: d.loc[0], minY: d.loc[1], maxX: d.loc[0], maxY: d.loc[1], data: d };
31093         }
31094
31095         // Replace or remove QAItem from rtree
31096         function updateRtree$1(item, replace) {
31097           _cache$1.rtree.remove(item, function (a, b) { return a.data.id === b.data.id; });
31098
31099           if (replace) {
31100             _cache$1.rtree.insert(item);
31101           }
31102         }
31103
31104         function linkErrorObject(d) {
31105           return ("<a class=\"error_object_link\">" + d + "</a>");
31106         }
31107
31108         function linkEntity(d) {
31109           return ("<a class=\"error_entity_link\">" + d + "</a>");
31110         }
31111
31112         function pointAverage(points) {
31113           if (points.length) {
31114             var sum = points.reduce(
31115               function (acc, point) { return geoVecAdd(acc, [point.lon, point.lat]); },
31116               [0,0]
31117             );
31118             return geoVecScale(sum, 1 / points.length);
31119           } else {
31120             return [0,0];
31121           }
31122         }
31123
31124         function relativeBearing(p1, p2) {
31125           var angle = Math.atan2(p2.lon - p1.lon, p2.lat - p1.lat);
31126           if (angle < 0) {
31127             angle += 2 * Math.PI;
31128           }
31129
31130           // Return degrees
31131           return angle * 180 / Math.PI;
31132         }
31133
31134         // Assuming range [0,360)
31135         function cardinalDirection(bearing) {
31136           var dir = 45 * Math.round(bearing / 45);
31137           var compass = {
31138             0: 'north',
31139             45: 'northeast',
31140             90: 'east',
31141             135: 'southeast',
31142             180: 'south',
31143             225: 'southwest',
31144             270: 'west',
31145             315: 'northwest',
31146             360: 'north'
31147           };
31148
31149           return _t(("QA.improveOSM.directions." + (compass[dir])));
31150         }
31151
31152         // Errors shouldn't obscure eachother
31153         function preventCoincident(loc, bumpUp) {
31154           var coincident = false;
31155           do {
31156             // first time, move marker up. after that, move marker right.
31157             var delta = coincident ? [0.00001, 0] : (bumpUp ? [0, 0.00001] : [0, 0]);
31158             loc = geoVecAdd(loc, delta);
31159             var bbox = geoExtent(loc).bbox();
31160             coincident = _cache$1.rtree.search(bbox).length;
31161           } while (coincident);
31162
31163           return loc;
31164         }
31165
31166         var serviceImproveOSM = {
31167           title: 'improveOSM',
31168
31169           init: function init() {
31170             _mainFileFetcher.get('qa_data')
31171               .then(function (d) { return _impOsmData = d.improveOSM; });
31172
31173             if (!_cache$1) {
31174               this.reset();
31175             }
31176
31177             this.event = utilRebind(this, dispatch$2, 'on');
31178           },
31179
31180           reset: function reset() {
31181             if (_cache$1) {
31182               Object.values(_cache$1.inflightTile).forEach(abortRequest$1);
31183             }
31184             _cache$1 = {
31185               data: {},
31186               loadedTile: {},
31187               inflightTile: {},
31188               inflightPost: {},
31189               closed: {},
31190               rtree: new RBush()
31191             };
31192           },
31193
31194           loadIssues: function loadIssues(projection) {
31195             var this$1 = this;
31196
31197             var options = {
31198               client: 'iD',
31199               status: 'OPEN',
31200               zoom: '19' // Use a high zoom so that clusters aren't returned
31201             };
31202
31203             // determine the needed tiles to cover the view
31204             var tiles = tiler$1
31205               .zoomExtent([_tileZoom$1, _tileZoom$1])
31206               .getTiles(projection);
31207
31208             // abort inflight requests that are no longer needed
31209             abortUnwantedRequests$1(_cache$1, tiles);
31210
31211             // issue new requests..
31212             tiles.forEach(function (tile) {
31213               if (_cache$1.loadedTile[tile.id] || _cache$1.inflightTile[tile.id]) { return; }
31214
31215               var ref = tile.extent.rectangle();
31216               var east = ref[0];
31217               var north = ref[1];
31218               var west = ref[2];
31219               var south = ref[3];
31220               var params = Object.assign({}, options, { east: east, south: south, west: west, north: north });
31221
31222               // 3 separate requests to store for each tile
31223               var requests = {};
31224
31225               Object.keys(_impOsmUrls).forEach(function (k) {
31226                 // We exclude WATER from missing geometry as it doesn't seem useful
31227                 // We use most confident one-way and turn restrictions only, still have false positives
31228                 var kParams = Object.assign({},
31229                   params,
31230                   (k === 'mr') ? { type: 'PARKING,ROAD,BOTH,PATH' } : { confidenceLevel: 'C1' }
31231                 );
31232                 var url = (_impOsmUrls[k]) + "/search?" + utilQsString(kParams);
31233                 var controller = new AbortController();
31234
31235                 requests[k] = controller;
31236
31237                 d3_json(url, { signal: controller.signal })
31238                   .then(function (data) {
31239                     delete _cache$1.inflightTile[tile.id][k];
31240                     if (!Object.keys(_cache$1.inflightTile[tile.id]).length) {
31241                       delete _cache$1.inflightTile[tile.id];
31242                       _cache$1.loadedTile[tile.id] = true;
31243                     }
31244
31245                     // Road segments at high zoom == oneways
31246                     if (data.roadSegments) {
31247                       data.roadSegments.forEach(function (feature) {
31248                         // Position error at the approximate middle of the segment
31249                         var points = feature.points;
31250                         var wayId = feature.wayId;
31251                         var fromNodeId = feature.fromNodeId;
31252                         var toNodeId = feature.toNodeId;
31253                         var itemId = "" + wayId + fromNodeId + toNodeId;
31254                         var mid = points.length / 2;
31255                         var loc;
31256
31257                         // Even number of points, find midpoint of the middle two
31258                         // Odd number of points, use position of very middle point
31259                         if (mid % 1 === 0) {
31260                           loc = pointAverage([points[mid - 1], points[mid]]);
31261                         } else {
31262                           mid = points[Math.floor(mid)];
31263                           loc = [mid.lon, mid.lat];
31264                         }
31265
31266                         // One-ways can land on same segment in opposite direction
31267                         loc = preventCoincident(loc, false);
31268
31269                         var d = new QAItem(loc, this$1, k, itemId, {
31270                           issueKey: k, // used as a category
31271                           identifier: { // used to post changes
31272                             wayId: wayId,
31273                             fromNodeId: fromNodeId,
31274                             toNodeId: toNodeId
31275                           },
31276                           objectId: wayId,
31277                           objectType: 'way'
31278                         });
31279
31280                         // Variables used in the description
31281                         d.replacements = {
31282                           percentage: feature.percentOfTrips,
31283                           num_trips: feature.numberOfTrips,
31284                           highway: linkErrorObject(_t('QA.keepRight.error_parts.highway')),
31285                           from_node: linkEntity('n' + feature.fromNodeId),
31286                           to_node: linkEntity('n' + feature.toNodeId)
31287                         };
31288
31289                         _cache$1.data[d.id] = d;
31290                         _cache$1.rtree.insert(encodeIssueRtree$1(d));
31291                       });
31292                     }
31293
31294                     // Tiles at high zoom == missing roads
31295                     if (data.tiles) {
31296                       data.tiles.forEach(function (feature) {
31297                         var type = feature.type;
31298                         var x = feature.x;
31299                         var y = feature.y;
31300                         var numberOfTrips = feature.numberOfTrips;
31301                         var geoType = type.toLowerCase();
31302                         var itemId = "" + geoType + x + y + numberOfTrips;
31303
31304                         // Average of recorded points should land on the missing geometry
31305                         // Missing geometry could happen to land on another error
31306                         var loc = pointAverage(feature.points);
31307                         loc = preventCoincident(loc, false);
31308
31309                         var d = new QAItem(loc, this$1, (k + "-" + geoType), itemId, {
31310                           issueKey: k,
31311                           identifier: { x: x, y: y }
31312                         });
31313
31314                         d.replacements = {
31315                           num_trips: numberOfTrips,
31316                           geometry_type: _t(("QA.improveOSM.geometry_types." + geoType))
31317                         };
31318
31319                         // -1 trips indicates data came from a 3rd party
31320                         if (numberOfTrips === -1) {
31321                           d.desc = _t('QA.improveOSM.error_types.mr.description_alt', d.replacements);
31322                         }
31323
31324                         _cache$1.data[d.id] = d;
31325                         _cache$1.rtree.insert(encodeIssueRtree$1(d));
31326                       });
31327                     }
31328
31329                     // Entities at high zoom == turn restrictions
31330                     if (data.entities) {
31331                       data.entities.forEach(function (feature) {
31332                         var point = feature.point;
31333                         var id = feature.id;
31334                         var segments = feature.segments;
31335                         var numberOfPasses = feature.numberOfPasses;
31336                         var turnType = feature.turnType;
31337                         var itemId = "" + (id.replace(/[,:+#]/g, '_'));
31338
31339                         // Turn restrictions could be missing at same junction
31340                         // We also want to bump the error up so node is accessible
31341                         var loc = preventCoincident([point.lon, point.lat], true);
31342
31343                         // Elements are presented in a strange way
31344                         var ids = id.split(',');
31345                         var from_way = ids[0];
31346                         var via_node = ids[3];
31347                         var to_way = ids[2].split(':')[1];
31348
31349                         var d = new QAItem(loc, this$1, k, itemId, {
31350                           issueKey: k,
31351                           identifier: id,
31352                           objectId: via_node,
31353                           objectType: 'node'
31354                         });
31355
31356                         // Travel direction along from_way clarifies the turn restriction
31357                         var ref = segments[0].points;
31358                         var p1 = ref[0];
31359                         var p2 = ref[1];
31360                         var dir_of_travel = cardinalDirection(relativeBearing(p1, p2));
31361
31362                         // Variables used in the description
31363                         d.replacements = {
31364                           num_passed: numberOfPasses,
31365                           num_trips: segments[0].numberOfTrips,
31366                           turn_restriction: turnType.toLowerCase(),
31367                           from_way: linkEntity('w' + from_way),
31368                           to_way: linkEntity('w' + to_way),
31369                           travel_direction: dir_of_travel,
31370                           junction: linkErrorObject(_t('QA.keepRight.error_parts.this_node'))
31371                         };
31372
31373                         _cache$1.data[d.id] = d;
31374                         _cache$1.rtree.insert(encodeIssueRtree$1(d));
31375                         dispatch$2.call('loaded');
31376                       });
31377                     }
31378                   })
31379                   .catch(function () {
31380                     delete _cache$1.inflightTile[tile.id][k];
31381                     if (!Object.keys(_cache$1.inflightTile[tile.id]).length) {
31382                       delete _cache$1.inflightTile[tile.id];
31383                       _cache$1.loadedTile[tile.id] = true;
31384                     }
31385                   });
31386               });
31387
31388               _cache$1.inflightTile[tile.id] = requests;
31389             });
31390           },
31391
31392           getComments: function getComments(item) {
31393             var this$1 = this;
31394
31395             // If comments already retrieved no need to do so again
31396             if (item.comments) {
31397               return Promise.resolve(item);
31398             }
31399
31400             var key = item.issueKey;
31401             var qParams = {};
31402
31403             if (key === 'ow') {
31404               qParams = item.identifier;
31405             } else if (key === 'mr') {
31406               qParams.tileX = item.identifier.x;
31407               qParams.tileY = item.identifier.y;
31408             } else if (key === 'tr') {
31409               qParams.targetId = item.identifier;
31410             }
31411
31412             var url = (_impOsmUrls[key]) + "/retrieveComments?" + utilQsString(qParams);
31413             var cacheComments = function (data) {
31414               // Assign directly for immediate use afterwards
31415               // comments are served newest to oldest
31416               item.comments = data.comments ? data.comments.reverse() : [];
31417               this$1.replaceItem(item);
31418             };
31419
31420             return d3_json(url).then(cacheComments).then(function () { return item; });
31421           },
31422
31423           postUpdate: function postUpdate(d, callback) {
31424             if (!serviceOsm.authenticated()) { // Username required in payload
31425               return callback({ message: 'Not Authenticated', status: -3}, d);
31426             }
31427             if (_cache$1.inflightPost[d.id]) {
31428               return callback({ message: 'Error update already inflight', status: -2 }, d);
31429             }
31430
31431             // Payload can only be sent once username is established
31432             serviceOsm.userDetails(sendPayload.bind(this));
31433
31434             function sendPayload(err, user) {
31435               var this$1 = this;
31436
31437               if (err) { return callback(err, d); }
31438
31439               var key = d.issueKey;
31440               var url = (_impOsmUrls[key]) + "/comment";
31441               var payload = {
31442                 username: user.display_name,
31443                 targetIds: [ d.identifier ]
31444               };
31445
31446               if (d.newStatus) {
31447                 payload.status = d.newStatus;
31448                 payload.text = 'status changed';
31449               }
31450
31451               // Comment take place of default text
31452               if (d.newComment) {
31453                 payload.text = d.newComment;
31454               }
31455
31456               var controller = new AbortController();
31457               _cache$1.inflightPost[d.id] = controller;
31458
31459               var options = {
31460                 method: 'POST',
31461                 signal: controller.signal,
31462                 body: JSON.stringify(payload)
31463               };
31464
31465               d3_json(url, options)
31466                 .then(function () {
31467                   delete _cache$1.inflightPost[d.id];
31468
31469                   // Just a comment, update error in cache
31470                   if (!d.newStatus) {
31471                     var now = new Date();
31472                     var comments = d.comments ? d.comments : [];
31473
31474                     comments.push({
31475                       username: payload.username,
31476                       text: payload.text,
31477                       timestamp: now.getTime() / 1000
31478                     });
31479
31480                     this$1.replaceItem(d.update({
31481                       comments: comments,
31482                       newComment: undefined
31483                     }));
31484                   } else {
31485                     this$1.removeItem(d);
31486                     if (d.newStatus === 'SOLVED') {
31487                       // Keep track of the number of issues closed per type to tag the changeset
31488                       if (!(d.issueKey in _cache$1.closed)) {
31489                         _cache$1.closed[d.issueKey] = 0;
31490                       }
31491                       _cache$1.closed[d.issueKey] += 1;
31492                     }
31493                   }
31494                   if (callback) { callback(null, d); }
31495                 })
31496                 .catch(function (err) {
31497                   delete _cache$1.inflightPost[d.id];
31498                   if (callback) { callback(err.message); }
31499                 });
31500             }
31501           },
31502
31503
31504           // Get all cached QAItems covering the viewport
31505           getItems: function getItems(projection) {
31506             var viewport = projection.clipExtent();
31507             var min = [viewport[0][0], viewport[1][1]];
31508             var max = [viewport[1][0], viewport[0][1]];
31509             var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
31510
31511             return _cache$1.rtree.search(bbox).map(function (d) { return d.data; });
31512           },
31513
31514           // Get a QAItem from cache
31515           // NOTE: Don't change method name until UI v3 is merged
31516           getError: function getError(id) {
31517             return _cache$1.data[id];
31518           },
31519
31520           // get the name of the icon to display for this item
31521           getIcon: function getIcon(itemType) {
31522             return _impOsmData.icons[itemType];
31523           },
31524
31525           // Replace a single QAItem in the cache
31526           replaceItem: function replaceItem(issue) {
31527             if (!(issue instanceof QAItem) || !issue.id) { return; }
31528
31529             _cache$1.data[issue.id] = issue;
31530             updateRtree$1(encodeIssueRtree$1(issue), true); // true = replace
31531             return issue;
31532           },
31533
31534           // Remove a single QAItem from the cache
31535           removeItem: function removeItem(issue) {
31536             if (!(issue instanceof QAItem) || !issue.id) { return; }
31537
31538             delete _cache$1.data[issue.id];
31539             updateRtree$1(encodeIssueRtree$1(issue), false); // false = remove
31540           },
31541
31542           // Used to populate `closed:improveosm:*` changeset tags
31543           getClosedCounts: function getClosedCounts() {
31544             return _cache$1.closed;
31545           }
31546         };
31547
31548         var defaults = createCommonjsModule(function (module) {
31549         function getDefaults() {
31550           return {
31551             baseUrl: null,
31552             breaks: false,
31553             gfm: true,
31554             headerIds: true,
31555             headerPrefix: '',
31556             highlight: null,
31557             langPrefix: 'language-',
31558             mangle: true,
31559             pedantic: false,
31560             renderer: null,
31561             sanitize: false,
31562             sanitizer: null,
31563             silent: false,
31564             smartLists: false,
31565             smartypants: false,
31566             tokenizer: null,
31567             xhtml: false
31568           };
31569         }
31570
31571         function changeDefaults(newDefaults) {
31572           module.exports.defaults = newDefaults;
31573         }
31574
31575         module.exports = {
31576           defaults: getDefaults(),
31577           getDefaults: getDefaults,
31578           changeDefaults: changeDefaults
31579         };
31580         });
31581
31582         /**
31583          * Helpers
31584          */
31585         var escapeTest = /[&<>"']/;
31586         var escapeReplace = /[&<>"']/g;
31587         var escapeTestNoEncode = /[<>"']|&(?!#?\w+;)/;
31588         var escapeReplaceNoEncode = /[<>"']|&(?!#?\w+;)/g;
31589         var escapeReplacements = {
31590           '&': '&amp;',
31591           '<': '&lt;',
31592           '>': '&gt;',
31593           '"': '&quot;',
31594           "'": '&#39;'
31595         };
31596         var getEscapeReplacement = function (ch) { return escapeReplacements[ch]; };
31597         function escape$1(html, encode) {
31598           if (encode) {
31599             if (escapeTest.test(html)) {
31600               return html.replace(escapeReplace, getEscapeReplacement);
31601             }
31602           } else {
31603             if (escapeTestNoEncode.test(html)) {
31604               return html.replace(escapeReplaceNoEncode, getEscapeReplacement);
31605             }
31606           }
31607
31608           return html;
31609         }
31610
31611         var unescapeTest = /&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/ig;
31612
31613         function unescape$1(html) {
31614           // explicitly match decimal, hex, and named HTML entities
31615           return html.replace(unescapeTest, function (_, n) {
31616             n = n.toLowerCase();
31617             if (n === 'colon') { return ':'; }
31618             if (n.charAt(0) === '#') {
31619               return n.charAt(1) === 'x'
31620                 ? String.fromCharCode(parseInt(n.substring(2), 16))
31621                 : String.fromCharCode(+n.substring(1));
31622             }
31623             return '';
31624           });
31625         }
31626
31627         var caret = /(^|[^\[])\^/g;
31628         function edit(regex, opt) {
31629           regex = regex.source || regex;
31630           opt = opt || '';
31631           var obj = {
31632             replace: function (name, val) {
31633               val = val.source || val;
31634               val = val.replace(caret, '$1');
31635               regex = regex.replace(name, val);
31636               return obj;
31637             },
31638             getRegex: function () {
31639               return new RegExp(regex, opt);
31640             }
31641           };
31642           return obj;
31643         }
31644
31645         var nonWordAndColonTest = /[^\w:]/g;
31646         var originIndependentUrl = /^$|^[a-z][a-z0-9+.-]*:|^[?#]/i;
31647         function cleanUrl(sanitize, base, href) {
31648           if (sanitize) {
31649             var prot;
31650             try {
31651               prot = decodeURIComponent(unescape$1(href))
31652                 .replace(nonWordAndColonTest, '')
31653                 .toLowerCase();
31654             } catch (e) {
31655               return null;
31656             }
31657             if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0 || prot.indexOf('data:') === 0) {
31658               return null;
31659             }
31660           }
31661           if (base && !originIndependentUrl.test(href)) {
31662             href = resolveUrl(base, href);
31663           }
31664           try {
31665             href = encodeURI(href).replace(/%25/g, '%');
31666           } catch (e$1) {
31667             return null;
31668           }
31669           return href;
31670         }
31671
31672         var baseUrls = {};
31673         var justDomain = /^[^:]+:\/*[^/]*$/;
31674         var protocol = /^([^:]+:)[\s\S]*$/;
31675         var domain = /^([^:]+:\/*[^/]*)[\s\S]*$/;
31676
31677         function resolveUrl(base, href) {
31678           if (!baseUrls[' ' + base]) {
31679             // we can ignore everything in base after the last slash of its path component,
31680             // but we might need to add _that_
31681             // https://tools.ietf.org/html/rfc3986#section-3
31682             if (justDomain.test(base)) {
31683               baseUrls[' ' + base] = base + '/';
31684             } else {
31685               baseUrls[' ' + base] = rtrim(base, '/', true);
31686             }
31687           }
31688           base = baseUrls[' ' + base];
31689           var relativeBase = base.indexOf(':') === -1;
31690
31691           if (href.substring(0, 2) === '//') {
31692             if (relativeBase) {
31693               return href;
31694             }
31695             return base.replace(protocol, '$1') + href;
31696           } else if (href.charAt(0) === '/') {
31697             if (relativeBase) {
31698               return href;
31699             }
31700             return base.replace(domain, '$1') + href;
31701           } else {
31702             return base + href;
31703           }
31704         }
31705
31706         var noopTest = { exec: function noopTest() {} };
31707
31708         function merge$1(obj) {
31709           var arguments$1 = arguments;
31710
31711           var i = 1,
31712             target,
31713             key;
31714
31715           for (; i < arguments.length; i++) {
31716             target = arguments$1[i];
31717             for (key in target) {
31718               if (Object.prototype.hasOwnProperty.call(target, key)) {
31719                 obj[key] = target[key];
31720               }
31721             }
31722           }
31723
31724           return obj;
31725         }
31726
31727         function splitCells(tableRow, count) {
31728           // ensure that every cell-delimiting pipe has a space
31729           // before it to distinguish it from an escaped pipe
31730           var row = tableRow.replace(/\|/g, function (match, offset, str) {
31731               var escaped = false,
31732                 curr = offset;
31733               while (--curr >= 0 && str[curr] === '\\') { escaped = !escaped; }
31734               if (escaped) {
31735                 // odd number of slashes means | is escaped
31736                 // so we leave it alone
31737                 return '|';
31738               } else {
31739                 // add space before unescaped |
31740                 return ' |';
31741               }
31742             }),
31743             cells = row.split(/ \|/);
31744           var i = 0;
31745
31746           if (cells.length > count) {
31747             cells.splice(count);
31748           } else {
31749             while (cells.length < count) { cells.push(''); }
31750           }
31751
31752           for (; i < cells.length; i++) {
31753             // leading or trailing whitespace is ignored per the gfm spec
31754             cells[i] = cells[i].trim().replace(/\\\|/g, '|');
31755           }
31756           return cells;
31757         }
31758
31759         // Remove trailing 'c's. Equivalent to str.replace(/c*$/, '').
31760         // /c*$/ is vulnerable to REDOS.
31761         // invert: Remove suffix of non-c chars instead. Default falsey.
31762         function rtrim(str, c, invert) {
31763           var l = str.length;
31764           if (l === 0) {
31765             return '';
31766           }
31767
31768           // Length of suffix matching the invert condition.
31769           var suffLen = 0;
31770
31771           // Step left until we fail to match the invert condition.
31772           while (suffLen < l) {
31773             var currChar = str.charAt(l - suffLen - 1);
31774             if (currChar === c && !invert) {
31775               suffLen++;
31776             } else if (currChar !== c && invert) {
31777               suffLen++;
31778             } else {
31779               break;
31780             }
31781           }
31782
31783           return str.substr(0, l - suffLen);
31784         }
31785
31786         function findClosingBracket(str, b) {
31787           if (str.indexOf(b[1]) === -1) {
31788             return -1;
31789           }
31790           var l = str.length;
31791           var level = 0,
31792             i = 0;
31793           for (; i < l; i++) {
31794             if (str[i] === '\\') {
31795               i++;
31796             } else if (str[i] === b[0]) {
31797               level++;
31798             } else if (str[i] === b[1]) {
31799               level--;
31800               if (level < 0) {
31801                 return i;
31802               }
31803             }
31804           }
31805           return -1;
31806         }
31807
31808         function checkSanitizeDeprecation(opt) {
31809           if (opt && opt.sanitize && !opt.silent) {
31810             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');
31811           }
31812         }
31813
31814         var helpers = {
31815           escape: escape$1,
31816           unescape: unescape$1,
31817           edit: edit,
31818           cleanUrl: cleanUrl,
31819           resolveUrl: resolveUrl,
31820           noopTest: noopTest,
31821           merge: merge$1,
31822           splitCells: splitCells,
31823           rtrim: rtrim,
31824           findClosingBracket: findClosingBracket,
31825           checkSanitizeDeprecation: checkSanitizeDeprecation
31826         };
31827
31828         var defaults$1 = defaults.defaults;
31829         var rtrim$1 = helpers.rtrim;
31830         var splitCells$1 = helpers.splitCells;
31831         var escape$2 = helpers.escape;
31832         var findClosingBracket$1 = helpers.findClosingBracket;
31833
31834         function outputLink(cap, link, raw) {
31835           var href = link.href;
31836           var title = link.title ? escape$2(link.title) : null;
31837
31838           if (cap[0].charAt(0) !== '!') {
31839             return {
31840               type: 'link',
31841               raw: raw,
31842               href: href,
31843               title: title,
31844               text: cap[1]
31845             };
31846           } else {
31847             return {
31848               type: 'image',
31849               raw: raw,
31850               text: escape$2(cap[1]),
31851               href: href,
31852               title: title
31853             };
31854           }
31855         }
31856
31857         /**
31858          * Tokenizer
31859          */
31860         var Tokenizer_1 = /*@__PURE__*/(function () {
31861           function Tokenizer(options) {
31862             this.options = options || defaults$1;
31863           }
31864
31865           Tokenizer.prototype.space = function space (src) {
31866             var cap = this.rules.block.newline.exec(src);
31867             if (cap) {
31868               if (cap[0].length > 1) {
31869                 return {
31870                   type: 'space',
31871                   raw: cap[0]
31872                 };
31873               }
31874               return { raw: '\n' };
31875             }
31876           };
31877
31878           Tokenizer.prototype.code = function code (src, tokens) {
31879             var cap = this.rules.block.code.exec(src);
31880             if (cap) {
31881               var lastToken = tokens[tokens.length - 1];
31882               // An indented code block cannot interrupt a paragraph.
31883               if (lastToken && lastToken.type === 'paragraph') {
31884                 tokens.pop();
31885                 lastToken.text += '\n' + cap[0].trimRight();
31886                 lastToken.raw += '\n' + cap[0];
31887                 return lastToken;
31888               } else {
31889                 var text = cap[0].replace(/^ {4}/gm, '');
31890                 return {
31891                   type: 'code',
31892                   raw: cap[0],
31893                   codeBlockStyle: 'indented',
31894                   text: !this.options.pedantic
31895                     ? rtrim$1(text, '\n')
31896                     : text
31897                 };
31898               }
31899             }
31900           };
31901
31902           Tokenizer.prototype.fences = function fences (src) {
31903             var cap = this.rules.block.fences.exec(src);
31904             if (cap) {
31905               return {
31906                 type: 'code',
31907                 raw: cap[0],
31908                 lang: cap[2] ? cap[2].trim() : cap[2],
31909                 text: cap[3] || ''
31910               };
31911             }
31912           };
31913
31914           Tokenizer.prototype.heading = function heading (src) {
31915             var cap = this.rules.block.heading.exec(src);
31916             if (cap) {
31917               return {
31918                 type: 'heading',
31919                 raw: cap[0],
31920                 depth: cap[1].length,
31921                 text: cap[2]
31922               };
31923             }
31924           };
31925
31926           Tokenizer.prototype.nptable = function nptable (src) {
31927             var cap = this.rules.block.nptable.exec(src);
31928             if (cap) {
31929               var item = {
31930                 type: 'table',
31931                 header: splitCells$1(cap[1].replace(/^ *| *\| *$/g, '')),
31932                 align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
31933                 cells: cap[3] ? cap[3].replace(/\n$/, '').split('\n') : [],
31934                 raw: cap[0]
31935               };
31936
31937               if (item.header.length === item.align.length) {
31938                 var l = item.align.length;
31939                 var i;
31940                 for (i = 0; i < l; i++) {
31941                   if (/^ *-+: *$/.test(item.align[i])) {
31942                     item.align[i] = 'right';
31943                   } else if (/^ *:-+: *$/.test(item.align[i])) {
31944                     item.align[i] = 'center';
31945                   } else if (/^ *:-+ *$/.test(item.align[i])) {
31946                     item.align[i] = 'left';
31947                   } else {
31948                     item.align[i] = null;
31949                   }
31950                 }
31951
31952                 l = item.cells.length;
31953                 for (i = 0; i < l; i++) {
31954                   item.cells[i] = splitCells$1(item.cells[i], item.header.length);
31955                 }
31956
31957                 return item;
31958               }
31959             }
31960           };
31961
31962           Tokenizer.prototype.hr = function hr (src) {
31963             var cap = this.rules.block.hr.exec(src);
31964             if (cap) {
31965               return {
31966                 type: 'hr',
31967                 raw: cap[0]
31968               };
31969             }
31970           };
31971
31972           Tokenizer.prototype.blockquote = function blockquote (src) {
31973             var cap = this.rules.block.blockquote.exec(src);
31974             if (cap) {
31975               var text = cap[0].replace(/^ *> ?/gm, '');
31976
31977               return {
31978                 type: 'blockquote',
31979                 raw: cap[0],
31980                 text: text
31981               };
31982             }
31983           };
31984
31985           Tokenizer.prototype.list = function list (src) {
31986             var cap = this.rules.block.list.exec(src);
31987             if (cap) {
31988               var raw = cap[0];
31989               var bull = cap[2];
31990               var isordered = bull.length > 1;
31991
31992               var list = {
31993                 type: 'list',
31994                 raw: raw,
31995                 ordered: isordered,
31996                 start: isordered ? +bull : '',
31997                 loose: false,
31998                 items: []
31999               };
32000
32001               // Get each top-level item.
32002               var itemMatch = cap[0].match(this.rules.block.item);
32003
32004               var next = false,
32005                 item,
32006                 space,
32007                 b,
32008                 addBack,
32009                 loose,
32010                 istask,
32011                 ischecked;
32012
32013               var l = itemMatch.length;
32014               for (var i = 0; i < l; i++) {
32015                 item = itemMatch[i];
32016                 raw = item;
32017
32018                 // Remove the list item's bullet
32019                 // so it is seen as the next token.
32020                 space = item.length;
32021                 item = item.replace(/^ *([*+-]|\d+\.) */, '');
32022
32023                 // Outdent whatever the
32024                 // list item contains. Hacky.
32025                 if (~item.indexOf('\n ')) {
32026                   space -= item.length;
32027                   item = !this.options.pedantic
32028                     ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
32029                     : item.replace(/^ {1,4}/gm, '');
32030                 }
32031
32032                 // Determine whether the next list item belongs here.
32033                 // Backpedal if it does not belong in this list.
32034                 if (i !== l - 1) {
32035                   b = this.rules.block.bullet.exec(itemMatch[i + 1])[0];
32036                   if (bull.length > 1 ? b.length === 1
32037                     : (b.length > 1 || (this.options.smartLists && b !== bull))) {
32038                     addBack = itemMatch.slice(i + 1).join('\n');
32039                     list.raw = list.raw.substring(0, list.raw.length - addBack.length);
32040                     i = l - 1;
32041                   }
32042                 }
32043
32044                 // Determine whether item is loose or not.
32045                 // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
32046                 // for discount behavior.
32047                 loose = next || /\n\n(?!\s*$)/.test(item);
32048                 if (i !== l - 1) {
32049                   next = item.charAt(item.length - 1) === '\n';
32050                   if (!loose) { loose = next; }
32051                 }
32052
32053                 if (loose) {
32054                   list.loose = true;
32055                 }
32056
32057                 // Check for task list items
32058                 istask = /^\[[ xX]\] /.test(item);
32059                 ischecked = undefined;
32060                 if (istask) {
32061                   ischecked = item[1] !== ' ';
32062                   item = item.replace(/^\[[ xX]\] +/, '');
32063                 }
32064
32065                 list.items.push({
32066                   raw: raw,
32067                   task: istask,
32068                   checked: ischecked,
32069                   loose: loose,
32070                   text: item
32071                 });
32072               }
32073
32074               return list;
32075             }
32076           };
32077
32078           Tokenizer.prototype.html = function html (src) {
32079             var cap = this.rules.block.html.exec(src);
32080             if (cap) {
32081               return {
32082                 type: this.options.sanitize
32083                   ? 'paragraph'
32084                   : 'html',
32085                 raw: cap[0],
32086                 pre: !this.options.sanitizer
32087                   && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
32088                 text: this.options.sanitize ? (this.options.sanitizer ? this.options.sanitizer(cap[0]) : escape$2(cap[0])) : cap[0]
32089               };
32090             }
32091           };
32092
32093           Tokenizer.prototype.def = function def (src) {
32094             var cap = this.rules.block.def.exec(src);
32095             if (cap) {
32096               if (cap[3]) { cap[3] = cap[3].substring(1, cap[3].length - 1); }
32097               var tag = cap[1].toLowerCase().replace(/\s+/g, ' ');
32098               return {
32099                 tag: tag,
32100                 raw: cap[0],
32101                 href: cap[2],
32102                 title: cap[3]
32103               };
32104             }
32105           };
32106
32107           Tokenizer.prototype.table = function table (src) {
32108             var cap = this.rules.block.table.exec(src);
32109             if (cap) {
32110               var item = {
32111                 type: 'table',
32112                 header: splitCells$1(cap[1].replace(/^ *| *\| *$/g, '')),
32113                 align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
32114                 cells: cap[3] ? cap[3].replace(/\n$/, '').split('\n') : []
32115               };
32116
32117               if (item.header.length === item.align.length) {
32118                 item.raw = cap[0];
32119
32120                 var l = item.align.length;
32121                 var i;
32122                 for (i = 0; i < l; i++) {
32123                   if (/^ *-+: *$/.test(item.align[i])) {
32124                     item.align[i] = 'right';
32125                   } else if (/^ *:-+: *$/.test(item.align[i])) {
32126                     item.align[i] = 'center';
32127                   } else if (/^ *:-+ *$/.test(item.align[i])) {
32128                     item.align[i] = 'left';
32129                   } else {
32130                     item.align[i] = null;
32131                   }
32132                 }
32133
32134                 l = item.cells.length;
32135                 for (i = 0; i < l; i++) {
32136                   item.cells[i] = splitCells$1(
32137                     item.cells[i].replace(/^ *\| *| *\| *$/g, ''),
32138                     item.header.length);
32139                 }
32140
32141                 return item;
32142               }
32143             }
32144           };
32145
32146           Tokenizer.prototype.lheading = function lheading (src) {
32147             var cap = this.rules.block.lheading.exec(src);
32148             if (cap) {
32149               return {
32150                 type: 'heading',
32151                 raw: cap[0],
32152                 depth: cap[2].charAt(0) === '=' ? 1 : 2,
32153                 text: cap[1]
32154               };
32155             }
32156           };
32157
32158           Tokenizer.prototype.paragraph = function paragraph (src) {
32159             var cap = this.rules.block.paragraph.exec(src);
32160             if (cap) {
32161               return {
32162                 type: 'paragraph',
32163                 raw: cap[0],
32164                 text: cap[1].charAt(cap[1].length - 1) === '\n'
32165                   ? cap[1].slice(0, -1)
32166                   : cap[1]
32167               };
32168             }
32169           };
32170
32171           Tokenizer.prototype.text = function text (src) {
32172             var cap = this.rules.block.text.exec(src);
32173             if (cap) {
32174               return {
32175                 type: 'text',
32176                 raw: cap[0],
32177                 text: cap[0]
32178               };
32179             }
32180           };
32181
32182           Tokenizer.prototype.escape = function escape$1 (src) {
32183             var cap = this.rules.inline.escape.exec(src);
32184             if (cap) {
32185               return {
32186                 type: 'escape',
32187                 raw: cap[0],
32188                 text: escape$2(cap[1])
32189               };
32190             }
32191           };
32192
32193           Tokenizer.prototype.tag = function tag (src, inLink, inRawBlock) {
32194             var cap = this.rules.inline.tag.exec(src);
32195             if (cap) {
32196               if (!inLink && /^<a /i.test(cap[0])) {
32197                 inLink = true;
32198               } else if (inLink && /^<\/a>/i.test(cap[0])) {
32199                 inLink = false;
32200               }
32201               if (!inRawBlock && /^<(pre|code|kbd|script)(\s|>)/i.test(cap[0])) {
32202                 inRawBlock = true;
32203               } else if (inRawBlock && /^<\/(pre|code|kbd|script)(\s|>)/i.test(cap[0])) {
32204                 inRawBlock = false;
32205               }
32206
32207               return {
32208                 type: this.options.sanitize
32209                   ? 'text'
32210                   : 'html',
32211                 raw: cap[0],
32212                 inLink: inLink,
32213                 inRawBlock: inRawBlock,
32214                 text: this.options.sanitize
32215                   ? (this.options.sanitizer
32216                     ? this.options.sanitizer(cap[0])
32217                     : escape$2(cap[0]))
32218                   : cap[0]
32219               };
32220             }
32221           };
32222
32223           Tokenizer.prototype.link = function link (src) {
32224             var cap = this.rules.inline.link.exec(src);
32225             if (cap) {
32226               var lastParenIndex = findClosingBracket$1(cap[2], '()');
32227               if (lastParenIndex > -1) {
32228                 var start = cap[0].indexOf('!') === 0 ? 5 : 4;
32229                 var linkLen = start + cap[1].length + lastParenIndex;
32230                 cap[2] = cap[2].substring(0, lastParenIndex);
32231                 cap[0] = cap[0].substring(0, linkLen).trim();
32232                 cap[3] = '';
32233               }
32234               var href = cap[2];
32235               var title = '';
32236               if (this.options.pedantic) {
32237                 var link = /^([^'"]*[^\s])\s+(['"])(.*)\2/.exec(href);
32238
32239                 if (link) {
32240                   href = link[1];
32241                   title = link[3];
32242                 } else {
32243                   title = '';
32244                 }
32245               } else {
32246                 title = cap[3] ? cap[3].slice(1, -1) : '';
32247               }
32248               href = href.trim().replace(/^<([\s\S]*)>$/, '$1');
32249               var token = outputLink(cap, {
32250                 href: href ? href.replace(this.rules.inline._escapes, '$1') : href,
32251                 title: title ? title.replace(this.rules.inline._escapes, '$1') : title
32252               }, cap[0]);
32253               return token;
32254             }
32255           };
32256
32257           Tokenizer.prototype.reflink = function reflink (src, links) {
32258             var cap;
32259             if ((cap = this.rules.inline.reflink.exec(src))
32260                 || (cap = this.rules.inline.nolink.exec(src))) {
32261               var link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
32262               link = links[link.toLowerCase()];
32263               if (!link || !link.href) {
32264                 var text = cap[0].charAt(0);
32265                 return {
32266                   type: 'text',
32267                   raw: text,
32268                   text: text
32269                 };
32270               }
32271               var token = outputLink(cap, link, cap[0]);
32272               return token;
32273             }
32274           };
32275
32276           Tokenizer.prototype.strong = function strong (src) {
32277             var cap = this.rules.inline.strong.exec(src);
32278             if (cap) {
32279               return {
32280                 type: 'strong',
32281                 raw: cap[0],
32282                 text: cap[4] || cap[3] || cap[2] || cap[1]
32283               };
32284             }
32285           };
32286
32287           Tokenizer.prototype.em = function em (src) {
32288             var cap = this.rules.inline.em.exec(src);
32289             if (cap) {
32290               return {
32291                 type: 'em',
32292                 raw: cap[0],
32293                 text: cap[6] || cap[5] || cap[4] || cap[3] || cap[2] || cap[1]
32294               };
32295             }
32296           };
32297
32298           Tokenizer.prototype.codespan = function codespan (src) {
32299             var cap = this.rules.inline.code.exec(src);
32300             if (cap) {
32301               return {
32302                 type: 'codespan',
32303                 raw: cap[0],
32304                 text: escape$2(cap[2].trim(), true)
32305               };
32306             }
32307           };
32308
32309           Tokenizer.prototype.br = function br (src) {
32310             var cap = this.rules.inline.br.exec(src);
32311             if (cap) {
32312               return {
32313                 type: 'br',
32314                 raw: cap[0]
32315               };
32316             }
32317           };
32318
32319           Tokenizer.prototype.del = function del (src) {
32320             var cap = this.rules.inline.del.exec(src);
32321             if (cap) {
32322               return {
32323                 type: 'del',
32324                 raw: cap[0],
32325                 text: cap[1]
32326               };
32327             }
32328           };
32329
32330           Tokenizer.prototype.autolink = function autolink (src, mangle) {
32331             var cap = this.rules.inline.autolink.exec(src);
32332             if (cap) {
32333               var text, href;
32334               if (cap[2] === '@') {
32335                 text = escape$2(this.options.mangle ? mangle(cap[1]) : cap[1]);
32336                 href = 'mailto:' + text;
32337               } else {
32338                 text = escape$2(cap[1]);
32339                 href = text;
32340               }
32341
32342               return {
32343                 type: 'link',
32344                 raw: cap[0],
32345                 text: text,
32346                 href: href,
32347                 tokens: [
32348                   {
32349                     type: 'text',
32350                     raw: text,
32351                     text: text
32352                   }
32353                 ]
32354               };
32355             }
32356           };
32357
32358           Tokenizer.prototype.url = function url (src, mangle) {
32359             var cap;
32360             if (cap = this.rules.inline.url.exec(src)) {
32361               var text, href;
32362               if (cap[2] === '@') {
32363                 text = escape$2(this.options.mangle ? mangle(cap[0]) : cap[0]);
32364                 href = 'mailto:' + text;
32365               } else {
32366                 // do extended autolink path validation
32367                 var prevCapZero;
32368                 do {
32369                   prevCapZero = cap[0];
32370                   cap[0] = this.rules.inline._backpedal.exec(cap[0])[0];
32371                 } while (prevCapZero !== cap[0]);
32372                 text = escape$2(cap[0]);
32373                 if (cap[1] === 'www.') {
32374                   href = 'http://' + text;
32375                 } else {
32376                   href = text;
32377                 }
32378               }
32379               return {
32380                 type: 'link',
32381                 raw: cap[0],
32382                 text: text,
32383                 href: href,
32384                 tokens: [
32385                   {
32386                     type: 'text',
32387                     raw: text,
32388                     text: text
32389                   }
32390                 ]
32391               };
32392             }
32393           };
32394
32395           Tokenizer.prototype.inlineText = function inlineText (src, inRawBlock, smartypants) {
32396             var cap = this.rules.inline.text.exec(src);
32397             if (cap) {
32398               var text;
32399               if (inRawBlock) {
32400                 text = this.options.sanitize ? (this.options.sanitizer ? this.options.sanitizer(cap[0]) : escape$2(cap[0])) : cap[0];
32401               } else {
32402                 text = escape$2(this.options.smartypants ? smartypants(cap[0]) : cap[0]);
32403               }
32404               return {
32405                 type: 'text',
32406                 raw: cap[0],
32407                 text: text
32408               };
32409             }
32410           };
32411
32412           return Tokenizer;
32413         }());
32414
32415         var noopTest$1 = helpers.noopTest;
32416         var edit$1 = helpers.edit;
32417         var merge$2 = helpers.merge;
32418
32419         /**
32420          * Block-Level Grammar
32421          */
32422         var block = {
32423           newline: /^\n+/,
32424           code: /^( {4}[^\n]+\n*)+/,
32425           fences: /^ {0,3}(`{3,}(?=[^`\n]*\n)|~{3,})([^\n]*)\n(?:|([\s\S]*?)\n)(?: {0,3}\1[~`]* *(?:\n+|$)|$)/,
32426           hr: /^ {0,3}((?:- *){3,}|(?:_ *){3,}|(?:\* *){3,})(?:\n+|$)/,
32427           heading: /^ {0,3}(#{1,6}) +([^\n]*?)(?: +#+)? *(?:\n+|$)/,
32428           blockquote: /^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/,
32429           list: /^( {0,3})(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
32430           html: '^ {0,3}(?:' // optional indentation
32431             + '<(script|pre|style)[\\s>][\\s\\S]*?(?:</\\1>[^\\n]*\\n+|$)' // (1)
32432             + '|comment[^\\n]*(\\n+|$)' // (2)
32433             + '|<\\?[\\s\\S]*?\\?>\\n*' // (3)
32434             + '|<![A-Z][\\s\\S]*?>\\n*' // (4)
32435             + '|<!\\[CDATA\\[[\\s\\S]*?\\]\\]>\\n*' // (5)
32436             + '|</?(tag)(?: +|\\n|/?>)[\\s\\S]*?(?:\\n{2,}|$)' // (6)
32437             + '|<(?!script|pre|style)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:\\n{2,}|$)' // (7) open tag
32438             + '|</(?!script|pre|style)[a-z][\\w-]*\\s*>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:\\n{2,}|$)' // (7) closing tag
32439             + ')',
32440           def: /^ {0,3}\[(label)\]: *\n? *<?([^\s>]+)>?(?:(?: +\n? *| *\n *)(title))? *(?:\n+|$)/,
32441           nptable: noopTest$1,
32442           table: noopTest$1,
32443           lheading: /^([^\n]+)\n {0,3}(=+|-+) *(?:\n+|$)/,
32444           // regex template, placeholders will be replaced according to different paragraph
32445           // interruption rules of commonmark and the original markdown spec:
32446           _paragraph: /^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html)[^\n]+)*)/,
32447           text: /^[^\n]+/
32448         };
32449
32450         block._label = /(?!\s*\])(?:\\[\[\]]|[^\[\]])+/;
32451         block._title = /(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/;
32452         block.def = edit$1(block.def)
32453           .replace('label', block._label)
32454           .replace('title', block._title)
32455           .getRegex();
32456
32457         block.bullet = /(?:[*+-]|\d{1,9}\.)/;
32458         block.item = /^( *)(bull) ?[^\n]*(?:\n(?!\1bull ?)[^\n]*)*/;
32459         block.item = edit$1(block.item, 'gm')
32460           .replace(/bull/g, block.bullet)
32461           .getRegex();
32462
32463         block.list = edit$1(block.list)
32464           .replace(/bull/g, block.bullet)
32465           .replace('hr', '\\n+(?=\\1?(?:(?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$))')
32466           .replace('def', '\\n+(?=' + block.def.source + ')')
32467           .getRegex();
32468
32469         block._tag = 'address|article|aside|base|basefont|blockquote|body|caption'
32470           + '|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption'
32471           + '|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe'
32472           + '|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option'
32473           + '|p|param|section|source|summary|table|tbody|td|tfoot|th|thead|title|tr'
32474           + '|track|ul';
32475         block._comment = /<!--(?!-?>)[\s\S]*?-->/;
32476         block.html = edit$1(block.html, 'i')
32477           .replace('comment', block._comment)
32478           .replace('tag', block._tag)
32479           .replace('attribute', / +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/)
32480           .getRegex();
32481
32482         block.paragraph = edit$1(block._paragraph)
32483           .replace('hr', block.hr)
32484           .replace('heading', ' {0,3}#{1,6} ')
32485           .replace('|lheading', '') // setex headings don't interrupt commonmark paragraphs
32486           .replace('blockquote', ' {0,3}>')
32487           .replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n')
32488           .replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt
32489           .replace('html', '</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|!--)')
32490           .replace('tag', block._tag) // pars can be interrupted by type (6) html blocks
32491           .getRegex();
32492
32493         block.blockquote = edit$1(block.blockquote)
32494           .replace('paragraph', block.paragraph)
32495           .getRegex();
32496
32497         /**
32498          * Normal Block Grammar
32499          */
32500
32501         block.normal = merge$2({}, block);
32502
32503         /**
32504          * GFM Block Grammar
32505          */
32506
32507         block.gfm = merge$2({}, block.normal, {
32508           nptable: '^ *([^|\\n ].*\\|.*)\\n' // Header
32509             + ' *([-:]+ *\\|[-| :]*)' // Align
32510             + '(?:\\n((?:(?!\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)', // Cells
32511           table: '^ *\\|(.+)\\n' // Header
32512             + ' *\\|?( *[-:]+[-| :]*)' // Align
32513             + '(?:\\n *((?:(?!\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)' // Cells
32514         });
32515
32516         block.gfm.nptable = edit$1(block.gfm.nptable)
32517           .replace('hr', block.hr)
32518           .replace('heading', ' {0,3}#{1,6} ')
32519           .replace('blockquote', ' {0,3}>')
32520           .replace('code', ' {4}[^\\n]')
32521           .replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n')
32522           .replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt
32523           .replace('html', '</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|!--)')
32524           .replace('tag', block._tag) // tables can be interrupted by type (6) html blocks
32525           .getRegex();
32526
32527         block.gfm.table = edit$1(block.gfm.table)
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         /**
32539          * Pedantic grammar (original John Gruber's loose markdown specification)
32540          */
32541
32542         block.pedantic = merge$2({}, block.normal, {
32543           html: edit$1(
32544             '^ *(?:comment *(?:\\n|\\s*$)'
32545             + '|<(tag)[\\s\\S]+?</\\1> *(?:\\n{2,}|\\s*$)' // closed tag
32546             + '|<tag(?:"[^"]*"|\'[^\']*\'|\\s[^\'"/>\\s]*)*?/?> *(?:\\n{2,}|\\s*$))')
32547             .replace('comment', block._comment)
32548             .replace(/tag/g, '(?!(?:'
32549               + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub'
32550               + '|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)'
32551               + '\\b)\\w+(?!:|[^\\w\\s@]*@)\\b')
32552             .getRegex(),
32553           def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/,
32554           heading: /^ *(#{1,6}) *([^\n]+?) *(?:#+ *)?(?:\n+|$)/,
32555           fences: noopTest$1, // fences not supported
32556           paragraph: edit$1(block.normal._paragraph)
32557             .replace('hr', block.hr)
32558             .replace('heading', ' *#{1,6} *[^\n]')
32559             .replace('lheading', block.lheading)
32560             .replace('blockquote', ' {0,3}>')
32561             .replace('|fences', '')
32562             .replace('|list', '')
32563             .replace('|html', '')
32564             .getRegex()
32565         });
32566
32567         /**
32568          * Inline-Level Grammar
32569          */
32570         var inline = {
32571           escape: /^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/,
32572           autolink: /^<(scheme:[^\s\x00-\x1f<>]*|email)>/,
32573           url: noopTest$1,
32574           tag: '^comment'
32575             + '|^</[a-zA-Z][\\w:-]*\\s*>' // self-closing tag
32576             + '|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>' // open tag
32577             + '|^<\\?[\\s\\S]*?\\?>' // processing instruction, e.g. <?php ?>
32578             + '|^<![a-zA-Z]+\\s[\\s\\S]*?>' // declaration, e.g. <!DOCTYPE html>
32579             + '|^<!\\[CDATA\\[[\\s\\S]*?\\]\\]>', // CDATA section
32580           link: /^!?\[(label)\]\(\s*(href)(?:\s+(title))?\s*\)/,
32581           reflink: /^!?\[(label)\]\[(?!\s*\])((?:\\[\[\]]?|[^\[\]\\])+)\]/,
32582           nolink: /^!?\[(?!\s*\])((?:\[[^\[\]]*\]|\\[\[\]]|[^\[\]])*)\](?:\[\])?/,
32583           strong: /^__([^\s_])__(?!_)|^\*\*([^\s*])\*\*(?!\*)|^__([^\s][\s\S]*?[^\s])__(?!_)|^\*\*([^\s][\s\S]*?[^\s])\*\*(?!\*)/,
32584           em: /^_([^\s_])_(?!_)|^_([^\s_<][\s\S]*?[^\s_])_(?!_|[^\spunctuation])|^_([^\s_<][\s\S]*?[^\s])_(?!_|[^\spunctuation])|^\*([^\s*<\[])\*(?!\*)|^\*([^\s<"][\s\S]*?[^\s\[\*])\*(?![\]`punctuation])|^\*([^\s*"<\[][\s\S]*[^\s])\*(?!\*)/,
32585           code: /^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/,
32586           br: /^( {2,}|\\)\n(?!\s*$)/,
32587           del: noopTest$1,
32588           text: /^(`+|[^`])(?:[\s\S]*?(?:(?=[\\<!\[`*]|\b_|$)|[^ ](?= {2,}\n))|(?= {2,}\n))/
32589         };
32590
32591         // list of punctuation marks from common mark spec
32592         // without ` and ] to workaround Rule 17 (inline code blocks/links)
32593         inline._punctuation = '!"#$%&\'()*+\\-./:;<=>?@\\[^_{|}~';
32594         inline.em = edit$1(inline.em).replace(/punctuation/g, inline._punctuation).getRegex();
32595
32596         inline._escapes = /\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/g;
32597
32598         inline._scheme = /[a-zA-Z][a-zA-Z0-9+.-]{1,31}/;
32599         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])?)+(?![-_])/;
32600         inline.autolink = edit$1(inline.autolink)
32601           .replace('scheme', inline._scheme)
32602           .replace('email', inline._email)
32603           .getRegex();
32604
32605         inline._attribute = /\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/;
32606
32607         inline.tag = edit$1(inline.tag)
32608           .replace('comment', block._comment)
32609           .replace('attribute', inline._attribute)
32610           .getRegex();
32611
32612         inline._label = /(?:\[[^\[\]]*\]|\\.|`[^`]*`|[^\[\]\\`])*?/;
32613         inline._href = /<(?:\\[<>]?|[^\s<>\\])*>|[^\s\x00-\x1f]*/;
32614         inline._title = /"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/;
32615
32616         inline.link = edit$1(inline.link)
32617           .replace('label', inline._label)
32618           .replace('href', inline._href)
32619           .replace('title', inline._title)
32620           .getRegex();
32621
32622         inline.reflink = edit$1(inline.reflink)
32623           .replace('label', inline._label)
32624           .getRegex();
32625
32626         /**
32627          * Normal Inline Grammar
32628          */
32629
32630         inline.normal = merge$2({}, inline);
32631
32632         /**
32633          * Pedantic Inline Grammar
32634          */
32635
32636         inline.pedantic = merge$2({}, inline.normal, {
32637           strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
32638           em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/,
32639           link: edit$1(/^!?\[(label)\]\((.*?)\)/)
32640             .replace('label', inline._label)
32641             .getRegex(),
32642           reflink: edit$1(/^!?\[(label)\]\s*\[([^\]]*)\]/)
32643             .replace('label', inline._label)
32644             .getRegex()
32645         });
32646
32647         /**
32648          * GFM Inline Grammar
32649          */
32650
32651         inline.gfm = merge$2({}, inline.normal, {
32652           escape: edit$1(inline.escape).replace('])', '~|])').getRegex(),
32653           _extended_email: /[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/,
32654           url: /^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/,
32655           _backpedal: /(?:[^?!.,:;*_~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_~)]+(?!$))+/,
32656           del: /^~+(?=\S)([\s\S]*?\S)~+/,
32657           text: /^(`+|[^`])(?:[\s\S]*?(?:(?=[\\<!\[`*~]|\b_|https?:\/\/|ftp:\/\/|www\.|$)|[^ ](?= {2,}\n)|[^a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-](?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@))|(?= {2,}\n|[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@))/
32658         });
32659
32660         inline.gfm.url = edit$1(inline.gfm.url, 'i')
32661           .replace('email', inline.gfm._extended_email)
32662           .getRegex();
32663         /**
32664          * GFM + Line Breaks Inline Grammar
32665          */
32666
32667         inline.breaks = merge$2({}, inline.gfm, {
32668           br: edit$1(inline.br).replace('{2,}', '*').getRegex(),
32669           text: edit$1(inline.gfm.text)
32670             .replace('\\b_', '\\b_| {2,}\\n')
32671             .replace(/\{2,\}/g, '*')
32672             .getRegex()
32673         });
32674
32675         var rules = {
32676           block: block,
32677           inline: inline
32678         };
32679
32680         var defaults$2 = defaults.defaults;
32681         var block$1 = rules.block;
32682         var inline$1 = rules.inline;
32683
32684         /**
32685          * smartypants text replacement
32686          */
32687         function smartypants(text) {
32688           return text
32689             // em-dashes
32690             .replace(/---/g, '\u2014')
32691             // en-dashes
32692             .replace(/--/g, '\u2013')
32693             // opening singles
32694             .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
32695             // closing singles & apostrophes
32696             .replace(/'/g, '\u2019')
32697             // opening doubles
32698             .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
32699             // closing doubles
32700             .replace(/"/g, '\u201d')
32701             // ellipses
32702             .replace(/\.{3}/g, '\u2026');
32703         }
32704
32705         /**
32706          * mangle email addresses
32707          */
32708         function mangle(text) {
32709           var out = '',
32710             i,
32711             ch;
32712
32713           var l = text.length;
32714           for (i = 0; i < l; i++) {
32715             ch = text.charCodeAt(i);
32716             if (Math.random() > 0.5) {
32717               ch = 'x' + ch.toString(16);
32718             }
32719             out += '&#' + ch + ';';
32720           }
32721
32722           return out;
32723         }
32724
32725         /**
32726          * Block Lexer
32727          */
32728         var Lexer_1 = /*@__PURE__*/(function () {
32729           function Lexer(options) {
32730             this.tokens = [];
32731             this.tokens.links = Object.create(null);
32732             this.options = options || defaults$2;
32733             this.options.tokenizer = this.options.tokenizer || new Tokenizer_1();
32734             this.tokenizer = this.options.tokenizer;
32735             this.tokenizer.options = this.options;
32736
32737             var rules = {
32738               block: block$1.normal,
32739               inline: inline$1.normal
32740             };
32741
32742             if (this.options.pedantic) {
32743               rules.block = block$1.pedantic;
32744               rules.inline = inline$1.pedantic;
32745             } else if (this.options.gfm) {
32746               rules.block = block$1.gfm;
32747               if (this.options.breaks) {
32748                 rules.inline = inline$1.breaks;
32749               } else {
32750                 rules.inline = inline$1.gfm;
32751               }
32752             }
32753             this.tokenizer.rules = rules;
32754           }
32755
32756           var staticAccessors = { rules: { configurable: true } };
32757
32758           /**
32759            * Expose Rules
32760            */
32761           staticAccessors.rules.get = function () {
32762             return {
32763               block: block$1,
32764               inline: inline$1
32765             };
32766           };
32767
32768           /**
32769            * Static Lex Method
32770            */
32771           Lexer.lex = function lex (src, options) {
32772             var lexer = new Lexer(options);
32773             return lexer.lex(src);
32774           };
32775
32776           /**
32777            * Preprocessing
32778            */
32779           Lexer.prototype.lex = function lex (src) {
32780             src = src
32781               .replace(/\r\n|\r/g, '\n')
32782               .replace(/\t/g, '    ');
32783
32784             this.blockTokens(src, this.tokens, true);
32785
32786             this.inline(this.tokens);
32787
32788             return this.tokens;
32789           };
32790
32791           /**
32792            * Lexing
32793            */
32794           Lexer.prototype.blockTokens = function blockTokens (src, tokens, top) {
32795             if ( tokens === void 0 ) tokens = [];
32796             if ( top === void 0 ) top = true;
32797
32798             src = src.replace(/^ +$/gm, '');
32799             var token, i, l;
32800
32801             while (src) {
32802               // newline
32803               if (token = this.tokenizer.space(src)) {
32804                 src = src.substring(token.raw.length);
32805                 if (token.type) {
32806                   tokens.push(token);
32807                 }
32808                 continue;
32809               }
32810
32811               // code
32812               if (token = this.tokenizer.code(src, tokens)) {
32813                 src = src.substring(token.raw.length);
32814                 tokens.push(token);
32815                 continue;
32816               }
32817
32818               // fences
32819               if (token = this.tokenizer.fences(src)) {
32820                 src = src.substring(token.raw.length);
32821                 tokens.push(token);
32822                 continue;
32823               }
32824
32825               // heading
32826               if (token = this.tokenizer.heading(src)) {
32827                 src = src.substring(token.raw.length);
32828                 tokens.push(token);
32829                 continue;
32830               }
32831
32832               // table no leading pipe (gfm)
32833               if (token = this.tokenizer.nptable(src)) {
32834                 src = src.substring(token.raw.length);
32835                 tokens.push(token);
32836                 continue;
32837               }
32838
32839               // hr
32840               if (token = this.tokenizer.hr(src)) {
32841                 src = src.substring(token.raw.length);
32842                 tokens.push(token);
32843                 continue;
32844               }
32845
32846               // blockquote
32847               if (token = this.tokenizer.blockquote(src)) {
32848                 src = src.substring(token.raw.length);
32849                 token.tokens = this.blockTokens(token.text, [], top);
32850                 tokens.push(token);
32851                 continue;
32852               }
32853
32854               // list
32855               if (token = this.tokenizer.list(src)) {
32856                 src = src.substring(token.raw.length);
32857                 l = token.items.length;
32858                 for (i = 0; i < l; i++) {
32859                   token.items[i].tokens = this.blockTokens(token.items[i].text, [], false);
32860                 }
32861                 tokens.push(token);
32862                 continue;
32863               }
32864
32865               // html
32866               if (token = this.tokenizer.html(src)) {
32867                 src = src.substring(token.raw.length);
32868                 tokens.push(token);
32869                 continue;
32870               }
32871
32872               // def
32873               if (top && (token = this.tokenizer.def(src))) {
32874                 src = src.substring(token.raw.length);
32875                 if (!this.tokens.links[token.tag]) {
32876                   this.tokens.links[token.tag] = {
32877                     href: token.href,
32878                     title: token.title
32879                   };
32880                 }
32881                 continue;
32882               }
32883
32884               // table (gfm)
32885               if (token = this.tokenizer.table(src)) {
32886                 src = src.substring(token.raw.length);
32887                 tokens.push(token);
32888                 continue;
32889               }
32890
32891               // lheading
32892               if (token = this.tokenizer.lheading(src)) {
32893                 src = src.substring(token.raw.length);
32894                 tokens.push(token);
32895                 continue;
32896               }
32897
32898               // top-level paragraph
32899               if (top && (token = this.tokenizer.paragraph(src))) {
32900                 src = src.substring(token.raw.length);
32901                 tokens.push(token);
32902                 continue;
32903               }
32904
32905               // text
32906               if (token = this.tokenizer.text(src)) {
32907                 src = src.substring(token.raw.length);
32908                 tokens.push(token);
32909                 continue;
32910               }
32911
32912               if (src) {
32913                 var errMsg = 'Infinite loop on byte: ' + src.charCodeAt(0);
32914                 if (this.options.silent) {
32915                   console.error(errMsg);
32916                   break;
32917                 } else {
32918                   throw new Error(errMsg);
32919                 }
32920               }
32921             }
32922
32923             return tokens;
32924           };
32925
32926           Lexer.prototype.inline = function inline (tokens) {
32927             var i,
32928               j,
32929               k,
32930               l2,
32931               row,
32932               token;
32933
32934             var l = tokens.length;
32935             for (i = 0; i < l; i++) {
32936               token = tokens[i];
32937               switch (token.type) {
32938                 case 'paragraph':
32939                 case 'text':
32940                 case 'heading': {
32941                   token.tokens = [];
32942                   this.inlineTokens(token.text, token.tokens);
32943                   break;
32944                 }
32945                 case 'table': {
32946                   token.tokens = {
32947                     header: [],
32948                     cells: []
32949                   };
32950
32951                   // header
32952                   l2 = token.header.length;
32953                   for (j = 0; j < l2; j++) {
32954                     token.tokens.header[j] = [];
32955                     this.inlineTokens(token.header[j], token.tokens.header[j]);
32956                   }
32957
32958                   // cells
32959                   l2 = token.cells.length;
32960                   for (j = 0; j < l2; j++) {
32961                     row = token.cells[j];
32962                     token.tokens.cells[j] = [];
32963                     for (k = 0; k < row.length; k++) {
32964                       token.tokens.cells[j][k] = [];
32965                       this.inlineTokens(row[k], token.tokens.cells[j][k]);
32966                     }
32967                   }
32968
32969                   break;
32970                 }
32971                 case 'blockquote': {
32972                   this.inline(token.tokens);
32973                   break;
32974                 }
32975                 case 'list': {
32976                   l2 = token.items.length;
32977                   for (j = 0; j < l2; j++) {
32978                     this.inline(token.items[j].tokens);
32979                   }
32980                   break;
32981                 }
32982               }
32983             }
32984
32985             return tokens;
32986           };
32987
32988           /**
32989            * Lexing/Compiling
32990            */
32991           Lexer.prototype.inlineTokens = function inlineTokens (src, tokens, inLink, inRawBlock) {
32992             if ( tokens === void 0 ) tokens = [];
32993             if ( inLink === void 0 ) inLink = false;
32994             if ( inRawBlock === void 0 ) inRawBlock = false;
32995
32996             var token;
32997
32998             while (src) {
32999               // escape
33000               if (token = this.tokenizer.escape(src)) {
33001                 src = src.substring(token.raw.length);
33002                 tokens.push(token);
33003                 continue;
33004               }
33005
33006               // tag
33007               if (token = this.tokenizer.tag(src, inLink, inRawBlock)) {
33008                 src = src.substring(token.raw.length);
33009                 inLink = token.inLink;
33010                 inRawBlock = token.inRawBlock;
33011                 tokens.push(token);
33012                 continue;
33013               }
33014
33015               // link
33016               if (token = this.tokenizer.link(src)) {
33017                 src = src.substring(token.raw.length);
33018                 if (token.type === 'link') {
33019                   token.tokens = this.inlineTokens(token.text, [], true, inRawBlock);
33020                 }
33021                 tokens.push(token);
33022                 continue;
33023               }
33024
33025               // reflink, nolink
33026               if (token = this.tokenizer.reflink(src, this.tokens.links)) {
33027                 src = src.substring(token.raw.length);
33028                 if (token.type === 'link') {
33029                   token.tokens = this.inlineTokens(token.text, [], true, inRawBlock);
33030                 }
33031                 tokens.push(token);
33032                 continue;
33033               }
33034
33035               // strong
33036               if (token = this.tokenizer.strong(src)) {
33037                 src = src.substring(token.raw.length);
33038                 token.tokens = this.inlineTokens(token.text, [], inLink, inRawBlock);
33039                 tokens.push(token);
33040                 continue;
33041               }
33042
33043               // em
33044               if (token = this.tokenizer.em(src)) {
33045                 src = src.substring(token.raw.length);
33046                 token.tokens = this.inlineTokens(token.text, [], inLink, inRawBlock);
33047                 tokens.push(token);
33048                 continue;
33049               }
33050
33051               // code
33052               if (token = this.tokenizer.codespan(src)) {
33053                 src = src.substring(token.raw.length);
33054                 tokens.push(token);
33055                 continue;
33056               }
33057
33058               // br
33059               if (token = this.tokenizer.br(src)) {
33060                 src = src.substring(token.raw.length);
33061                 tokens.push(token);
33062                 continue;
33063               }
33064
33065               // del (gfm)
33066               if (token = this.tokenizer.del(src)) {
33067                 src = src.substring(token.raw.length);
33068                 token.tokens = this.inlineTokens(token.text, [], inLink, inRawBlock);
33069                 tokens.push(token);
33070                 continue;
33071               }
33072
33073               // autolink
33074               if (token = this.tokenizer.autolink(src, mangle)) {
33075                 src = src.substring(token.raw.length);
33076                 tokens.push(token);
33077                 continue;
33078               }
33079
33080               // url (gfm)
33081               if (!inLink && (token = this.tokenizer.url(src, mangle))) {
33082                 src = src.substring(token.raw.length);
33083                 tokens.push(token);
33084                 continue;
33085               }
33086
33087               // text
33088               if (token = this.tokenizer.inlineText(src, inRawBlock, smartypants)) {
33089                 src = src.substring(token.raw.length);
33090                 tokens.push(token);
33091                 continue;
33092               }
33093
33094               if (src) {
33095                 var errMsg = 'Infinite loop on byte: ' + src.charCodeAt(0);
33096                 if (this.options.silent) {
33097                   console.error(errMsg);
33098                   break;
33099                 } else {
33100                   throw new Error(errMsg);
33101                 }
33102               }
33103             }
33104
33105             return tokens;
33106           };
33107
33108           Object.defineProperties( Lexer, staticAccessors );
33109
33110           return Lexer;
33111         }());
33112
33113         var defaults$3 = defaults.defaults;
33114         var cleanUrl$1 = helpers.cleanUrl;
33115         var escape$3 = helpers.escape;
33116
33117         /**
33118          * Renderer
33119          */
33120         var Renderer_1 = /*@__PURE__*/(function () {
33121           function Renderer(options) {
33122             this.options = options || defaults$3;
33123           }
33124
33125           Renderer.prototype.code = function code (code$1, infostring, escaped) {
33126             var lang = (infostring || '').match(/\S*/)[0];
33127             if (this.options.highlight) {
33128               var out = this.options.highlight(code$1, lang);
33129               if (out != null && out !== code$1) {
33130                 escaped = true;
33131                 code$1 = out;
33132               }
33133             }
33134
33135             if (!lang) {
33136               return '<pre><code>'
33137                 + (escaped ? code$1 : escape$3(code$1, true))
33138                 + '</code></pre>';
33139             }
33140
33141             return '<pre><code class="'
33142               + this.options.langPrefix
33143               + escape$3(lang, true)
33144               + '">'
33145               + (escaped ? code$1 : escape$3(code$1, true))
33146               + '</code></pre>\n';
33147           };
33148
33149           Renderer.prototype.blockquote = function blockquote (quote) {
33150             return '<blockquote>\n' + quote + '</blockquote>\n';
33151           };
33152
33153           Renderer.prototype.html = function html (html$1) {
33154             return html$1;
33155           };
33156
33157           Renderer.prototype.heading = function heading (text, level, raw, slugger) {
33158             if (this.options.headerIds) {
33159               return '<h'
33160                 + level
33161                 + ' id="'
33162                 + this.options.headerPrefix
33163                 + slugger.slug(raw)
33164                 + '">'
33165                 + text
33166                 + '</h'
33167                 + level
33168                 + '>\n';
33169             }
33170             // ignore IDs
33171             return '<h' + level + '>' + text + '</h' + level + '>\n';
33172           };
33173
33174           Renderer.prototype.hr = function hr () {
33175             return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
33176           };
33177
33178           Renderer.prototype.list = function list (body, ordered, start) {
33179             var type = ordered ? 'ol' : 'ul',
33180               startatt = (ordered && start !== 1) ? (' start="' + start + '"') : '';
33181             return '<' + type + startatt + '>\n' + body + '</' + type + '>\n';
33182           };
33183
33184           Renderer.prototype.listitem = function listitem (text) {
33185             return '<li>' + text + '</li>\n';
33186           };
33187
33188           Renderer.prototype.checkbox = function checkbox (checked) {
33189             return '<input '
33190               + (checked ? 'checked="" ' : '')
33191               + 'disabled="" type="checkbox"'
33192               + (this.options.xhtml ? ' /' : '')
33193               + '> ';
33194           };
33195
33196           Renderer.prototype.paragraph = function paragraph (text) {
33197             return '<p>' + text + '</p>\n';
33198           };
33199
33200           Renderer.prototype.table = function table (header, body) {
33201             if (body) { body = '<tbody>' + body + '</tbody>'; }
33202
33203             return '<table>\n'
33204               + '<thead>\n'
33205               + header
33206               + '</thead>\n'
33207               + body
33208               + '</table>\n';
33209           };
33210
33211           Renderer.prototype.tablerow = function tablerow (content) {
33212             return '<tr>\n' + content + '</tr>\n';
33213           };
33214
33215           Renderer.prototype.tablecell = function tablecell (content, flags) {
33216             var type = flags.header ? 'th' : 'td';
33217             var tag = flags.align
33218               ? '<' + type + ' align="' + flags.align + '">'
33219               : '<' + type + '>';
33220             return tag + content + '</' + type + '>\n';
33221           };
33222
33223           // span level renderer
33224           Renderer.prototype.strong = function strong (text) {
33225             return '<strong>' + text + '</strong>';
33226           };
33227
33228           Renderer.prototype.em = function em (text) {
33229             return '<em>' + text + '</em>';
33230           };
33231
33232           Renderer.prototype.codespan = function codespan (text) {
33233             return '<code>' + text + '</code>';
33234           };
33235
33236           Renderer.prototype.br = function br () {
33237             return this.options.xhtml ? '<br/>' : '<br>';
33238           };
33239
33240           Renderer.prototype.del = function del (text) {
33241             return '<del>' + text + '</del>';
33242           };
33243
33244           Renderer.prototype.link = function link (href, title, text) {
33245             href = cleanUrl$1(this.options.sanitize, this.options.baseUrl, href);
33246             if (href === null) {
33247               return text;
33248             }
33249             var out = '<a href="' + escape$3(href) + '"';
33250             if (title) {
33251               out += ' title="' + title + '"';
33252             }
33253             out += '>' + text + '</a>';
33254             return out;
33255           };
33256
33257           Renderer.prototype.image = function image (href, title, text) {
33258             href = cleanUrl$1(this.options.sanitize, this.options.baseUrl, href);
33259             if (href === null) {
33260               return text;
33261             }
33262
33263             var out = '<img src="' + href + '" alt="' + text + '"';
33264             if (title) {
33265               out += ' title="' + title + '"';
33266             }
33267             out += this.options.xhtml ? '/>' : '>';
33268             return out;
33269           };
33270
33271           Renderer.prototype.text = function text (text$1) {
33272             return text$1;
33273           };
33274
33275           return Renderer;
33276         }());
33277
33278         /**
33279          * TextRenderer
33280          * returns only the textual part of the token
33281          */
33282         var TextRenderer_1 = /*@__PURE__*/(function () {
33283           function TextRenderer () {}
33284
33285           TextRenderer.prototype.strong = function strong (text) {
33286             return text;
33287           };
33288
33289           TextRenderer.prototype.em = function em (text) {
33290             return text;
33291           };
33292
33293           TextRenderer.prototype.codespan = function codespan (text) {
33294             return text;
33295           };
33296
33297           TextRenderer.prototype.del = function del (text) {
33298             return text;
33299           };
33300
33301           TextRenderer.prototype.html = function html (text) {
33302             return text;
33303           };
33304
33305           TextRenderer.prototype.text = function text (text$1) {
33306             return text$1;
33307           };
33308
33309           TextRenderer.prototype.link = function link (href, title, text) {
33310             return '' + text;
33311           };
33312
33313           TextRenderer.prototype.image = function image (href, title, text) {
33314             return '' + text;
33315           };
33316
33317           TextRenderer.prototype.br = function br () {
33318             return '';
33319           };
33320
33321           return TextRenderer;
33322         }());
33323
33324         /**
33325          * Slugger generates header id
33326          */
33327         var Slugger_1 = /*@__PURE__*/(function () {
33328           function Slugger() {
33329             this.seen = {};
33330           }
33331
33332           /**
33333            * Convert string to unique id
33334            */
33335           Slugger.prototype.slug = function slug (value) {
33336             var slug = value
33337               .toLowerCase()
33338               .trim()
33339               // remove html tags
33340               .replace(/<[!\/a-z].*?>/ig, '')
33341               // remove unwanted chars
33342               .replace(/[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,./:;<=>?@[\]^`{|}~]/g, '')
33343               .replace(/\s/g, '-');
33344
33345             if (this.seen.hasOwnProperty(slug)) {
33346               var originalSlug = slug;
33347               do {
33348                 this.seen[originalSlug]++;
33349                 slug = originalSlug + '-' + this.seen[originalSlug];
33350               } while (this.seen.hasOwnProperty(slug));
33351             }
33352             this.seen[slug] = 0;
33353
33354             return slug;
33355           };
33356
33357           return Slugger;
33358         }());
33359
33360         var defaults$4 = defaults.defaults;
33361         var unescape$2 = helpers.unescape;
33362
33363         /**
33364          * Parsing & Compiling
33365          */
33366         var Parser_1 = /*@__PURE__*/(function () {
33367           function Parser(options) {
33368             this.options = options || defaults$4;
33369             this.options.renderer = this.options.renderer || new Renderer_1();
33370             this.renderer = this.options.renderer;
33371             this.renderer.options = this.options;
33372             this.textRenderer = new TextRenderer_1();
33373             this.slugger = new Slugger_1();
33374           }
33375
33376           /**
33377            * Static Parse Method
33378            */
33379           Parser.parse = function parse (tokens, options) {
33380             var parser = new Parser(options);
33381             return parser.parse(tokens);
33382           };
33383
33384           /**
33385            * Parse Loop
33386            */
33387           Parser.prototype.parse = function parse (tokens, top) {
33388             if ( top === void 0 ) top = true;
33389
33390             var out = '',
33391               i,
33392               j,
33393               k,
33394               l2,
33395               l3,
33396               row,
33397               cell,
33398               header,
33399               body,
33400               token,
33401               ordered,
33402               start,
33403               loose,
33404               itemBody,
33405               item,
33406               checked,
33407               task,
33408               checkbox;
33409
33410             var l = tokens.length;
33411             for (i = 0; i < l; i++) {
33412               token = tokens[i];
33413               switch (token.type) {
33414                 case 'space': {
33415                   continue;
33416                 }
33417                 case 'hr': {
33418                   out += this.renderer.hr();
33419                   continue;
33420                 }
33421                 case 'heading': {
33422                   out += this.renderer.heading(
33423                     this.parseInline(token.tokens),
33424                     token.depth,
33425                     unescape$2(this.parseInline(token.tokens, this.textRenderer)),
33426                     this.slugger);
33427                   continue;
33428                 }
33429                 case 'code': {
33430                   out += this.renderer.code(token.text,
33431                     token.lang,
33432                     token.escaped);
33433                   continue;
33434                 }
33435                 case 'table': {
33436                   header = '';
33437
33438                   // header
33439                   cell = '';
33440                   l2 = token.header.length;
33441                   for (j = 0; j < l2; j++) {
33442                     cell += this.renderer.tablecell(
33443                       this.parseInline(token.tokens.header[j]),
33444                       { header: true, align: token.align[j] }
33445                     );
33446                   }
33447                   header += this.renderer.tablerow(cell);
33448
33449                   body = '';
33450                   l2 = token.cells.length;
33451                   for (j = 0; j < l2; j++) {
33452                     row = token.tokens.cells[j];
33453
33454                     cell = '';
33455                     l3 = row.length;
33456                     for (k = 0; k < l3; k++) {
33457                       cell += this.renderer.tablecell(
33458                         this.parseInline(row[k]),
33459                         { header: false, align: token.align[k] }
33460                       );
33461                     }
33462
33463                     body += this.renderer.tablerow(cell);
33464                   }
33465                   out += this.renderer.table(header, body);
33466                   continue;
33467                 }
33468                 case 'blockquote': {
33469                   body = this.parse(token.tokens);
33470                   out += this.renderer.blockquote(body);
33471                   continue;
33472                 }
33473                 case 'list': {
33474                   ordered = token.ordered;
33475                   start = token.start;
33476                   loose = token.loose;
33477                   l2 = token.items.length;
33478
33479                   body = '';
33480                   for (j = 0; j < l2; j++) {
33481                     item = token.items[j];
33482                     checked = item.checked;
33483                     task = item.task;
33484
33485                     itemBody = '';
33486                     if (item.task) {
33487                       checkbox = this.renderer.checkbox(checked);
33488                       if (loose) {
33489                         if (item.tokens[0].type === 'text') {
33490                           item.tokens[0].text = checkbox + ' ' + item.tokens[0].text;
33491                           if (item.tokens[0].tokens && item.tokens[0].tokens.length > 0 && item.tokens[0].tokens[0].type === 'text') {
33492                             item.tokens[0].tokens[0].text = checkbox + ' ' + item.tokens[0].tokens[0].text;
33493                           }
33494                         } else {
33495                           item.tokens.unshift({
33496                             type: 'text',
33497                             text: checkbox
33498                           });
33499                         }
33500                       } else {
33501                         itemBody += checkbox;
33502                       }
33503                     }
33504
33505                     itemBody += this.parse(item.tokens, loose);
33506                     body += this.renderer.listitem(itemBody, task, checked);
33507                   }
33508
33509                   out += this.renderer.list(body, ordered, start);
33510                   continue;
33511                 }
33512                 case 'html': {
33513                   // TODO parse inline content if parameter markdown=1
33514                   out += this.renderer.html(token.text);
33515                   continue;
33516                 }
33517                 case 'paragraph': {
33518                   out += this.renderer.paragraph(this.parseInline(token.tokens));
33519                   continue;
33520                 }
33521                 case 'text': {
33522                   body = token.tokens ? this.parseInline(token.tokens) : token.text;
33523                   while (i + 1 < l && tokens[i + 1].type === 'text') {
33524                     token = tokens[++i];
33525                     body += '\n' + (token.tokens ? this.parseInline(token.tokens) : token.text);
33526                   }
33527                   out += top ? this.renderer.paragraph(body) : body;
33528                   continue;
33529                 }
33530                 default: {
33531                   var errMsg = 'Token with "' + token.type + '" type was not found.';
33532                   if (this.options.silent) {
33533                     console.error(errMsg);
33534                     return;
33535                   } else {
33536                     throw new Error(errMsg);
33537                   }
33538                 }
33539               }
33540             }
33541
33542             return out;
33543           };
33544
33545           /**
33546            * Parse Inline Tokens
33547            */
33548           Parser.prototype.parseInline = function parseInline (tokens, renderer) {
33549             renderer = renderer || this.renderer;
33550             var out = '',
33551               i,
33552               token;
33553
33554             var l = tokens.length;
33555             for (i = 0; i < l; i++) {
33556               token = tokens[i];
33557               switch (token.type) {
33558                 case 'escape': {
33559                   out += renderer.text(token.text);
33560                   break;
33561                 }
33562                 case 'html': {
33563                   out += renderer.html(token.text);
33564                   break;
33565                 }
33566                 case 'link': {
33567                   out += renderer.link(token.href, token.title, this.parseInline(token.tokens, renderer));
33568                   break;
33569                 }
33570                 case 'image': {
33571                   out += renderer.image(token.href, token.title, token.text);
33572                   break;
33573                 }
33574                 case 'strong': {
33575                   out += renderer.strong(this.parseInline(token.tokens, renderer));
33576                   break;
33577                 }
33578                 case 'em': {
33579                   out += renderer.em(this.parseInline(token.tokens, renderer));
33580                   break;
33581                 }
33582                 case 'codespan': {
33583                   out += renderer.codespan(token.text);
33584                   break;
33585                 }
33586                 case 'br': {
33587                   out += renderer.br();
33588                   break;
33589                 }
33590                 case 'del': {
33591                   out += renderer.del(this.parseInline(token.tokens, renderer));
33592                   break;
33593                 }
33594                 case 'text': {
33595                   out += renderer.text(token.text);
33596                   break;
33597                 }
33598                 default: {
33599                   var errMsg = 'Token with "' + token.type + '" type was not found.';
33600                   if (this.options.silent) {
33601                     console.error(errMsg);
33602                     return;
33603                   } else {
33604                     throw new Error(errMsg);
33605                   }
33606                 }
33607               }
33608             }
33609             return out;
33610           };
33611
33612           return Parser;
33613         }());
33614
33615         var merge$3 = helpers.merge;
33616         var checkSanitizeDeprecation$1 = helpers.checkSanitizeDeprecation;
33617         var escape$4 = helpers.escape;
33618         var getDefaults = defaults.getDefaults;
33619         var changeDefaults = defaults.changeDefaults;
33620         var defaults$5 = defaults.defaults;
33621
33622         /**
33623          * Marked
33624          */
33625         function marked(src, opt, callback) {
33626           // throw error in case of non string input
33627           if (typeof src === 'undefined' || src === null) {
33628             throw new Error('marked(): input parameter is undefined or null');
33629           }
33630           if (typeof src !== 'string') {
33631             throw new Error('marked(): input parameter is of type '
33632               + Object.prototype.toString.call(src) + ', string expected');
33633           }
33634
33635           if (callback || typeof opt === 'function') {
33636             if (!callback) {
33637               callback = opt;
33638               opt = null;
33639             }
33640
33641             opt = merge$3({}, marked.defaults, opt || {});
33642             checkSanitizeDeprecation$1(opt);
33643             var highlight = opt.highlight;
33644             var tokens,
33645               pending,
33646               i = 0;
33647
33648             try {
33649               tokens = Lexer_1.lex(src, opt);
33650             } catch (e) {
33651               return callback(e);
33652             }
33653
33654             pending = tokens.length;
33655
33656             var done = function(err) {
33657               if (err) {
33658                 opt.highlight = highlight;
33659                 return callback(err);
33660               }
33661
33662               var out;
33663
33664               try {
33665                 out = Parser_1.parse(tokens, opt);
33666               } catch (e) {
33667                 err = e;
33668               }
33669
33670               opt.highlight = highlight;
33671
33672               return err
33673                 ? callback(err)
33674                 : callback(null, out);
33675             };
33676
33677             if (!highlight || highlight.length < 3) {
33678               return done();
33679             }
33680
33681             delete opt.highlight;
33682
33683             if (!pending) { return done(); }
33684
33685             for (; i < tokens.length; i++) {
33686               (function(token) {
33687                 if (token.type !== 'code') {
33688                   return --pending || done();
33689                 }
33690                 return highlight(token.text, token.lang, function(err, code) {
33691                   if (err) { return done(err); }
33692                   if (code == null || code === token.text) {
33693                     return --pending || done();
33694                   }
33695                   token.text = code;
33696                   token.escaped = true;
33697                   --pending || done();
33698                 });
33699               })(tokens[i]);
33700             }
33701
33702             return;
33703           }
33704           try {
33705             opt = merge$3({}, marked.defaults, opt || {});
33706             checkSanitizeDeprecation$1(opt);
33707             return Parser_1.parse(Lexer_1.lex(src, opt), opt);
33708           } catch (e$1) {
33709             e$1.message += '\nPlease report this to https://github.com/markedjs/marked.';
33710             if ((opt || marked.defaults).silent) {
33711               return '<p>An error occurred:</p><pre>'
33712                 + escape$4(e$1.message + '', true)
33713                 + '</pre>';
33714             }
33715             throw e$1;
33716           }
33717         }
33718
33719         /**
33720          * Options
33721          */
33722
33723         marked.options =
33724         marked.setOptions = function(opt) {
33725           merge$3(marked.defaults, opt);
33726           changeDefaults(marked.defaults);
33727           return marked;
33728         };
33729
33730         marked.getDefaults = getDefaults;
33731
33732         marked.defaults = defaults$5;
33733
33734         /**
33735          * Use Extension
33736          */
33737
33738         marked.use = function(extension) {
33739           var opts = merge$3({}, extension);
33740           if (extension.renderer) {
33741             var renderer = marked.defaults.renderer || new Renderer_1();
33742             var loop = function ( prop ) {
33743               var prevRenderer = renderer[prop];
33744               renderer[prop] = function () {
33745                 var args = [], len = arguments.length;
33746                 while ( len-- ) args[ len ] = arguments[ len ];
33747
33748                 var ret = extension.renderer[prop].apply(renderer, args);
33749                 if (ret === false) {
33750                   ret = prevRenderer.apply(renderer, args);
33751                 }
33752                 return ret;
33753               };
33754             };
33755
33756             for (var prop in extension.renderer) loop( prop );
33757             opts.renderer = renderer;
33758           }
33759           if (extension.tokenizer) {
33760             var tokenizer = marked.defaults.tokenizer || new Tokenizer_1();
33761             var loop$1 = function ( prop ) {
33762               var prevTokenizer = tokenizer[prop$1];
33763               tokenizer[prop$1] = function () {
33764                 var args = [], len = arguments.length;
33765                 while ( len-- ) args[ len ] = arguments[ len ];
33766
33767                 var ret = extension.tokenizer[prop$1].apply(tokenizer, args);
33768                 if (ret === false) {
33769                   ret = prevTokenizer.apply(tokenizer, args);
33770                 }
33771                 return ret;
33772               };
33773             };
33774
33775             for (var prop$1 in extension.tokenizer) loop$1( prop );
33776             opts.tokenizer = tokenizer;
33777           }
33778           marked.setOptions(opts);
33779         };
33780
33781         /**
33782          * Expose
33783          */
33784
33785         marked.Parser = Parser_1;
33786         marked.parser = Parser_1.parse;
33787
33788         marked.Renderer = Renderer_1;
33789         marked.TextRenderer = TextRenderer_1;
33790
33791         marked.Lexer = Lexer_1;
33792         marked.lexer = Lexer_1.lex;
33793
33794         marked.Tokenizer = Tokenizer_1;
33795
33796         marked.Slugger = Slugger_1;
33797
33798         marked.parse = marked;
33799
33800         var marked_1 = marked;
33801
33802         var tiler$2 = utilTiler();
33803         var dispatch$3 = dispatch('loaded');
33804         var _tileZoom$2 = 14;
33805         var _osmoseUrlRoot = 'https://osmose.openstreetmap.fr/api/0.3';
33806         var _osmoseData = { icons: {}, items: [] };
33807
33808         // This gets reassigned if reset
33809         var _cache$2;
33810
33811         function abortRequest$2(controller) {
33812           if (controller) {
33813             controller.abort();
33814           }
33815         }
33816
33817         function abortUnwantedRequests$2(cache, tiles) {
33818           Object.keys(cache.inflightTile).forEach(function (k) {
33819             var wanted = tiles.find(function (tile) { return k === tile.id; });
33820             if (!wanted) {
33821               abortRequest$2(cache.inflightTile[k]);
33822               delete cache.inflightTile[k];
33823             }
33824           });
33825         }
33826
33827         function encodeIssueRtree$2(d) {
33828           return { minX: d.loc[0], minY: d.loc[1], maxX: d.loc[0], maxY: d.loc[1], data: d };
33829         }
33830
33831         // Replace or remove QAItem from rtree
33832         function updateRtree$2(item, replace) {
33833           _cache$2.rtree.remove(item, function (a, b) { return a.data.id === b.data.id; });
33834
33835           if (replace) {
33836             _cache$2.rtree.insert(item);
33837           }
33838         }
33839
33840         // Issues shouldn't obscure eachother
33841         function preventCoincident$1(loc) {
33842           var coincident = false;
33843           do {
33844             // first time, move marker up. after that, move marker right.
33845             var delta = coincident ? [0.00001, 0] : [0, 0.00001];
33846             loc = geoVecAdd(loc, delta);
33847             var bbox = geoExtent(loc).bbox();
33848             coincident = _cache$2.rtree.search(bbox).length;
33849           } while (coincident);
33850
33851           return loc;
33852         }
33853
33854         var serviceOsmose = {
33855           title: 'osmose',
33856
33857           init: function init() {
33858             _mainFileFetcher.get('qa_data')
33859               .then(function (d) {
33860                 _osmoseData = d.osmose;
33861                 _osmoseData.items = Object.keys(d.osmose.icons)
33862                   .map(function (s) { return s.split('-')[0]; })
33863                   .reduce(function (unique, item) { return unique.indexOf(item) !== -1 ? unique : unique.concat( [item]); }, []);
33864               });
33865
33866             if (!_cache$2) {
33867               this.reset();
33868             }
33869
33870             this.event = utilRebind(this, dispatch$3, 'on');
33871           },
33872
33873           reset: function reset() {
33874             var _strings = {};
33875             var _colors = {};
33876             if (_cache$2) {
33877               Object.values(_cache$2.inflightTile).forEach(abortRequest$2);
33878               // Strings and colors are static and should not be re-populated
33879               _strings = _cache$2.strings;
33880               _colors = _cache$2.colors;
33881             }
33882             _cache$2 = {
33883               data: {},
33884               loadedTile: {},
33885               inflightTile: {},
33886               inflightPost: {},
33887               closed: {},
33888               rtree: new RBush(),
33889               strings: _strings,
33890               colors: _colors
33891             };
33892           },
33893
33894           loadIssues: function loadIssues(projection) {
33895             var this$1 = this;
33896
33897             var params = {
33898               // Tiles return a maximum # of issues
33899               // So we want to filter our request for only types iD supports
33900               item: _osmoseData.items
33901             };
33902
33903             // determine the needed tiles to cover the view
33904             var tiles = tiler$2
33905               .zoomExtent([_tileZoom$2, _tileZoom$2])
33906               .getTiles(projection);
33907
33908             // abort inflight requests that are no longer needed
33909             abortUnwantedRequests$2(_cache$2, tiles);
33910
33911             // issue new requests..
33912             tiles.forEach(function (tile) {
33913               if (_cache$2.loadedTile[tile.id] || _cache$2.inflightTile[tile.id]) { return; }
33914
33915               var ref = tile.xyz;
33916               var x = ref[0];
33917               var y = ref[1];
33918               var z = ref[2];
33919               var url = _osmoseUrlRoot + "/issues/" + z + "/" + x + "/" + y + ".json?" + utilQsString(params);
33920
33921               var controller = new AbortController();
33922               _cache$2.inflightTile[tile.id] = controller;
33923
33924               d3_json(url, { signal: controller.signal })
33925                 .then(function (data) {
33926                   delete _cache$2.inflightTile[tile.id];
33927                   _cache$2.loadedTile[tile.id] = true;
33928
33929                   if (data.features) {
33930                     data.features.forEach(function (issue) {
33931                       var ref = issue.properties;
33932                       var item = ref.item;
33933                       var cl = ref.class;
33934                       var id = ref.uuid;
33935                       /* Osmose issues are uniquely identified by a unique
33936                         `item` and `class` combination (both integer values) */
33937                       var itemType = item + "-" + cl;
33938
33939                       // Filter out unsupported issue types (some are too specific or advanced)
33940                       if (itemType in _osmoseData.icons) {
33941                         var loc = issue.geometry.coordinates; // lon, lat
33942                         loc = preventCoincident$1(loc);
33943
33944                         var d = new QAItem(loc, this$1, itemType, id, { item: item });
33945
33946                         // Setting elems here prevents UI detail requests
33947                         if (item === 8300 || item === 8360) {
33948                           d.elems = [];
33949                         }
33950
33951                         _cache$2.data[d.id] = d;
33952                         _cache$2.rtree.insert(encodeIssueRtree$2(d));
33953                       }
33954                     });
33955                   }
33956
33957                   dispatch$3.call('loaded');
33958                 })
33959                 .catch(function () {
33960                   delete _cache$2.inflightTile[tile.id];
33961                   _cache$2.loadedTile[tile.id] = true;
33962                 });
33963             });
33964           },
33965
33966           loadIssueDetail: function loadIssueDetail(issue) {
33967             var this$1 = this;
33968
33969             // Issue details only need to be fetched once
33970             if (issue.elems !== undefined) {
33971               return Promise.resolve(issue);
33972             }
33973
33974             var url = _osmoseUrlRoot + "/issue/" + (issue.id) + "?langs=" + (_mainLocalizer.localeCode());
33975             var cacheDetails = function (data) {
33976               // Associated elements used for highlighting
33977               // Assign directly for immediate use in the callback
33978               issue.elems = data.elems.map(function (e) { return e.type.substring(0,1) + e.id; });
33979
33980               // Some issues have instance specific detail in a subtitle
33981               issue.detail = data.subtitle ? marked_1(data.subtitle.auto) : '';
33982
33983               this$1.replaceItem(issue);
33984             };
33985
33986             return d3_json(url).then(cacheDetails).then(function () { return issue; });
33987           },
33988
33989           loadStrings: function loadStrings(locale) {
33990             if ( locale === void 0 ) locale=_mainLocalizer.localeCode();
33991
33992             var items = Object.keys(_osmoseData.icons);
33993
33994             if (
33995               locale in _cache$2.strings
33996               && Object.keys(_cache$2.strings[locale]).length === items.length
33997             ) {
33998                 return Promise.resolve(_cache$2.strings[locale]);
33999             }
34000
34001             // May be partially populated already if some requests were successful
34002             if (!(locale in _cache$2.strings)) {
34003               _cache$2.strings[locale] = {};
34004             }
34005
34006             // Only need to cache strings for supported issue types
34007             // Using multiple individual item + class requests to reduce fetched data size
34008             var allRequests = items.map(function (itemType) {
34009               // No need to request data we already have
34010               if (itemType in _cache$2.strings[locale]) { return; }
34011
34012               var cacheData = function (data) {
34013                 // Bunch of nested single value arrays of objects
34014                 var ref = data.categories;
34015                 var cat = ref[0]; if ( cat === void 0 ) cat = {items:[]};
34016                 var ref$1 = cat.items;
34017                 var item = ref$1[0]; if ( item === void 0 ) item = {class:[]};
34018                 var ref$2 = item.class;
34019                 var cl = ref$2[0]; if ( cl === void 0 ) cl = null;
34020
34021                 // If null default value is reached, data wasn't as expected (or was empty)
34022                 if (!cl) {
34023                   /* eslint-disable no-console */
34024                   console.log(("Osmose strings request (" + itemType + ") had unexpected data"));
34025                   /* eslint-enable no-console */
34026                   return;
34027                 }
34028
34029                 // Cache served item colors to automatically style issue markers later
34030                 var itemInt = item.item;
34031                 var color = item.color;
34032                 if (/^#[A-Fa-f0-9]{6}|[A-Fa-f0-9]{3}/.test(color)) {
34033                   _cache$2.colors[itemInt] = color;
34034                 }
34035
34036                 // Value of root key will be null if no string exists
34037                 // If string exists, value is an object with key 'auto' for string
34038                 var title = cl.title;
34039                 var detail = cl.detail;
34040                 var fix = cl.fix;
34041                 var trap = cl.trap;
34042
34043                 // Osmose titles shouldn't contain markdown
34044                 var issueStrings = {};
34045                 if (title) { issueStrings.title = title.auto; }
34046                 if (detail) { issueStrings.detail = marked_1(detail.auto); }
34047                 if (trap) { issueStrings.trap = marked_1(trap.auto); }
34048                 if (fix) { issueStrings.fix = marked_1(fix.auto); }
34049
34050                 _cache$2.strings[locale][itemType] = issueStrings;
34051               };
34052
34053               var ref = itemType.split('-');
34054               var item = ref[0];
34055               var cl = ref[1];
34056
34057               // Osmose API falls back to English strings where untranslated or if locale doesn't exist
34058               var url = _osmoseUrlRoot + "/items/" + item + "/class/" + cl + "?langs=" + locale;
34059
34060               return d3_json(url).then(cacheData);
34061             });
34062
34063             return Promise.all(allRequests).then(function () { return _cache$2.strings[locale]; });
34064           },
34065
34066           getStrings: function getStrings(itemType, locale) {
34067             if ( locale === void 0 ) locale=_mainLocalizer.localeCode();
34068
34069             // No need to fallback to English, Osmose API handles this for us
34070             return (locale in _cache$2.strings) ? _cache$2.strings[locale][itemType] : {};
34071           },
34072
34073           getColor: function getColor(itemType) {
34074             return (itemType in _cache$2.colors) ? _cache$2.colors[itemType] : '#FFFFFF';
34075           },
34076
34077           postUpdate: function postUpdate(issue, callback) {
34078             var this$1 = this;
34079
34080             if (_cache$2.inflightPost[issue.id]) {
34081               return callback({ message: 'Issue update already inflight', status: -2 }, issue);
34082             }
34083
34084             // UI sets the status to either 'done' or 'false'
34085             var url = _osmoseUrlRoot + "/issue/" + (issue.id) + "/" + (issue.newStatus);
34086             var controller = new AbortController();
34087             var after = function () {
34088               delete _cache$2.inflightPost[issue.id];
34089
34090               this$1.removeItem(issue);
34091               if (issue.newStatus === 'done') {
34092                 // Keep track of the number of issues closed per `item` to tag the changeset
34093                 if (!(issue.item in _cache$2.closed)) {
34094                   _cache$2.closed[issue.item] = 0;
34095                 }
34096                 _cache$2.closed[issue.item] += 1;
34097               }
34098               if (callback) { callback(null, issue); }
34099             };
34100
34101             _cache$2.inflightPost[issue.id] = controller;
34102
34103             fetch(url, { signal: controller.signal })
34104               .then(after)
34105               .catch(function (err) {
34106                 delete _cache$2.inflightPost[issue.id];
34107                 if (callback) { callback(err.message); }
34108               });
34109           },
34110
34111           // Get all cached QAItems covering the viewport
34112           getItems: function getItems(projection) {
34113             var viewport = projection.clipExtent();
34114             var min = [viewport[0][0], viewport[1][1]];
34115             var max = [viewport[1][0], viewport[0][1]];
34116             var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
34117
34118             return _cache$2.rtree.search(bbox).map(function (d) { return d.data; });
34119           },
34120
34121           // Get a QAItem from cache
34122           // NOTE: Don't change method name until UI v3 is merged
34123           getError: function getError(id) {
34124             return _cache$2.data[id];
34125           },
34126
34127           // get the name of the icon to display for this item
34128           getIcon: function getIcon(itemType) {
34129             return _osmoseData.icons[itemType];
34130           },
34131
34132           // Replace a single QAItem in the cache
34133           replaceItem: function replaceItem(item) {
34134             if (!(item instanceof QAItem) || !item.id) { return; }
34135
34136             _cache$2.data[item.id] = item;
34137             updateRtree$2(encodeIssueRtree$2(item), true); // true = replace
34138             return item;
34139           },
34140
34141           // Remove a single QAItem from the cache
34142           removeItem: function removeItem(item) {
34143             if (!(item instanceof QAItem) || !item.id) { return; }
34144
34145             delete _cache$2.data[item.id];
34146             updateRtree$2(encodeIssueRtree$2(item), false); // false = remove
34147           },
34148
34149           // Used to populate `closed:osmose:*` changeset tags
34150           getClosedCounts: function getClosedCounts() {
34151             return _cache$2.closed;
34152           },
34153
34154           itemURL: function itemURL(item) {
34155             return ("https://osmose.openstreetmap.fr/en/error/" + (item.id));
34156           }
34157         };
34158
34159         /*
34160             A standalone SVG element that contains only a `defs` sub-element. To be
34161             used once globally, since defs IDs must be unique within a document.
34162         */
34163         function svgDefs(context) {
34164
34165             function drawDefs(selection) {
34166                 var defs = selection.append('defs');
34167
34168                 // add markers
34169                 defs
34170                     .append('marker')
34171                     .attr('id', 'ideditor-oneway-marker')
34172                     .attr('viewBox', '0 0 10 5')
34173                     .attr('refX', 2.5)
34174                     .attr('refY', 2.5)
34175                     .attr('markerWidth', 2)
34176                     .attr('markerHeight', 2)
34177                     .attr('markerUnits', 'strokeWidth')
34178                     .attr('orient', 'auto')
34179                     .append('path')
34180                     .attr('class', 'oneway-marker-path')
34181                     .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')
34182                     .attr('stroke', 'none')
34183                     .attr('fill', '#000')
34184                     .attr('opacity', '0.75');
34185
34186                 // SVG markers have to be given a colour where they're defined
34187                 // (they can't inherit it from the line they're attached to),
34188                 // so we need to manually define markers for each color of tag
34189                 // (also, it's slightly nicer if we can control the
34190                 // positioning for different tags)
34191                 function addSidedMarker(name, color, offset) {
34192                     defs
34193                         .append('marker')
34194                         .attr('id', 'ideditor-sided-marker-' + name)
34195                         .attr('viewBox', '0 0 2 2')
34196                         .attr('refX', 1)
34197                         .attr('refY', -offset)
34198                         .attr('markerWidth', 1.5)
34199                         .attr('markerHeight', 1.5)
34200                         .attr('markerUnits', 'strokeWidth')
34201                         .attr('orient', 'auto')
34202                         .append('path')
34203                         .attr('class', 'sided-marker-path sided-marker-' + name + '-path')
34204                         .attr('d', 'M 0,0 L 1,1 L 2,0 z')
34205                         .attr('stroke', 'none')
34206                         .attr('fill', color);
34207                 }
34208                 addSidedMarker('natural', 'rgb(170, 170, 170)', 0);
34209                 // for a coastline, the arrows are (somewhat unintuitively) on
34210                 // the water side, so let's color them blue (with a gap) to
34211                 // give a stronger indication
34212                 addSidedMarker('coastline', '#77dede', 1);
34213                 addSidedMarker('waterway', '#77dede', 1);
34214                 // barriers have a dashed line, and separating the triangle
34215                 // from the line visually suits that
34216                 addSidedMarker('barrier', '#ddd', 1);
34217                 addSidedMarker('man_made', '#fff', 0);
34218
34219                 defs
34220                     .append('marker')
34221                     .attr('id', 'ideditor-viewfield-marker')
34222                     .attr('viewBox', '0 0 16 16')
34223                     .attr('refX', 8)
34224                     .attr('refY', 16)
34225                     .attr('markerWidth', 4)
34226                     .attr('markerHeight', 4)
34227                     .attr('markerUnits', 'strokeWidth')
34228                     .attr('orient', 'auto')
34229                     .append('path')
34230                     .attr('class', 'viewfield-marker-path')
34231                     .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')
34232                     .attr('fill', '#333')
34233                     .attr('fill-opacity', '0.75')
34234                     .attr('stroke', '#fff')
34235                     .attr('stroke-width', '0.5px')
34236                     .attr('stroke-opacity', '0.75');
34237
34238                 defs
34239                     .append('marker')
34240                     .attr('id', 'ideditor-viewfield-marker-wireframe')
34241                     .attr('viewBox', '0 0 16 16')
34242                     .attr('refX', 8)
34243                     .attr('refY', 16)
34244                     .attr('markerWidth', 4)
34245                     .attr('markerHeight', 4)
34246                     .attr('markerUnits', 'strokeWidth')
34247                     .attr('orient', 'auto')
34248                     .append('path')
34249                     .attr('class', 'viewfield-marker-path')
34250                     .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')
34251                     .attr('fill', 'none')
34252                     .attr('stroke', '#fff')
34253                     .attr('stroke-width', '0.5px')
34254                     .attr('stroke-opacity', '0.75');
34255
34256                 // add patterns
34257                 var patterns = defs.selectAll('pattern')
34258                     .data([
34259                         // pattern name, pattern image name
34260                         ['beach', 'dots'],
34261                         ['construction', 'construction'],
34262                         ['cemetery', 'cemetery'],
34263                         ['cemetery_christian', 'cemetery_christian'],
34264                         ['cemetery_buddhist', 'cemetery_buddhist'],
34265                         ['cemetery_muslim', 'cemetery_muslim'],
34266                         ['cemetery_jewish', 'cemetery_jewish'],
34267                         ['farmland', 'farmland'],
34268                         ['farmyard', 'farmyard'],
34269                         ['forest', 'forest'],
34270                         ['forest_broadleaved', 'forest_broadleaved'],
34271                         ['forest_needleleaved', 'forest_needleleaved'],
34272                         ['forest_leafless', 'forest_leafless'],
34273                         ['golf_green', 'grass'],
34274                         ['grass', 'grass'],
34275                         ['landfill', 'landfill'],
34276                         ['meadow', 'grass'],
34277                         ['orchard', 'orchard'],
34278                         ['pond', 'pond'],
34279                         ['quarry', 'quarry'],
34280                         ['scrub', 'bushes'],
34281                         ['vineyard', 'vineyard'],
34282                         ['water_standing', 'lines'],
34283                         ['waves', 'waves'],
34284                         ['wetland', 'wetland'],
34285                         ['wetland_marsh', 'wetland_marsh'],
34286                         ['wetland_swamp', 'wetland_swamp'],
34287                         ['wetland_bog', 'wetland_bog'],
34288                         ['wetland_reedbed', 'wetland_reedbed']
34289                     ])
34290                     .enter()
34291                     .append('pattern')
34292                     .attr('id', function (d) { return 'ideditor-pattern-' + d[0]; })
34293                     .attr('width', 32)
34294                     .attr('height', 32)
34295                     .attr('patternUnits', 'userSpaceOnUse');
34296
34297                 patterns
34298                     .append('rect')
34299                     .attr('x', 0)
34300                     .attr('y', 0)
34301                     .attr('width', 32)
34302                     .attr('height', 32)
34303                     .attr('class', function (d) { return 'pattern-color-' + d[0]; });
34304
34305                 patterns
34306                     .append('image')
34307                     .attr('x', 0)
34308                     .attr('y', 0)
34309                     .attr('width', 32)
34310                     .attr('height', 32)
34311                     .attr('xlink:href', function (d) {
34312                         return context.imagePath('pattern/' + d[1] + '.png');
34313                     });
34314
34315                 // add clip paths
34316                 defs.selectAll('clipPath')
34317                     .data([12, 18, 20, 32, 45])
34318                     .enter()
34319                     .append('clipPath')
34320                     .attr('id', function (d) { return 'ideditor-clip-square-' + d; })
34321                     .append('rect')
34322                     .attr('x', 0)
34323                     .attr('y', 0)
34324                     .attr('width', function (d) { return d; })
34325                     .attr('height', function (d) { return d; });
34326
34327                 // add symbol spritesheets
34328                 defs
34329                     .call(drawDefs.addSprites, [
34330                         'iD-sprite', 'maki-sprite', 'temaki-sprite', 'fa-sprite', 'tnp-sprite', 'community-sprite'
34331                     ], true);
34332             }
34333
34334
34335             drawDefs.addSprites = function(selection, ids, overrideColors) {
34336                 var spritesheets = selection.selectAll('.spritesheet');
34337                 var currData = spritesheets.data();
34338                 var data = utilArrayUniq(currData.concat(ids));
34339
34340                 spritesheets
34341                     .data(data)
34342                     .enter()
34343                     .append('g')
34344                     .attr('class', function(d) { return 'spritesheet spritesheet-' + d; })
34345                     .each(function(d) {
34346                         var url = context.imagePath(d + '.svg');
34347                         var node = select(this).node();
34348
34349                         svg(url)
34350                             .then(function(svg) {
34351                                 node.appendChild(
34352                                     select(svg.documentElement).attr('id', 'ideditor-' + d).node()
34353                                 );
34354                                 if (overrideColors && d !== 'iD-sprite') {   // allow icon colors to be overridden..
34355                                     select(node).selectAll('path')
34356                                         .attr('fill', 'currentColor');
34357                                 }
34358                             })
34359                             .catch(function() {
34360                                 /* ignore */
34361                             });
34362                     });
34363             };
34364
34365
34366             return drawDefs;
34367         }
34368
34369         /* global Mapillary:false */
34370
34371
34372         var apibase = 'https://a.mapillary.com/v3/';
34373         var viewercss = 'mapillary-js/mapillary.min.css';
34374         var viewerjs = 'mapillary-js/mapillary.min.js';
34375         var clientId = 'NzNRM2otQkR2SHJzaXJmNmdQWVQ0dzo1ZWYyMmYwNjdmNDdlNmVi';
34376         var mapFeatureConfig = {
34377             values: [
34378                 'construction--flat--crosswalk-plain',
34379                 'marking--discrete--crosswalk-zebra',
34380                 'object--banner',
34381                 'object--bench',
34382                 'object--bike-rack',
34383                 'object--billboard',
34384                 'object--catch-basin',
34385                 'object--cctv-camera',
34386                 'object--fire-hydrant',
34387                 'object--mailbox',
34388                 'object--manhole',
34389                 'object--phone-booth',
34390                 'object--sign--advertisement',
34391                 'object--sign--information',
34392                 'object--sign--store',
34393                 'object--street-light',
34394                 'object--support--utility-pole',
34395                 'object--traffic-light--*',
34396                 'object--traffic-light--pedestrians',
34397                 'object--trash-can'
34398             ].join(',')
34399         };
34400         var maxResults = 1000;
34401         var tileZoom = 14;
34402         var tiler$3 = utilTiler().zoomExtent([tileZoom, tileZoom]).skipNullIsland(true);
34403         var dispatch$4 = dispatch('loadedImages', 'loadedSigns', 'loadedMapFeatures', 'bearingChanged');
34404         var _mlyFallback = false;
34405         var _mlyCache;
34406         var _mlyClicks;
34407         var _mlySelectedImageKey;
34408         var _mlyViewer;
34409
34410
34411         function abortRequest$3(controller) {
34412             controller.abort();
34413         }
34414
34415
34416         function maxPageAtZoom(z) {
34417             if (z < 15)   { return 2; }
34418             if (z === 15) { return 5; }
34419             if (z === 16) { return 10; }
34420             if (z === 17) { return 20; }
34421             if (z === 18) { return 40; }
34422             if (z > 18)   { return 80; }
34423         }
34424
34425
34426         function loadTiles(which, url, projection) {
34427             var currZoom = Math.floor(geoScaleToZoom(projection.scale()));
34428             var tiles = tiler$3.getTiles(projection);
34429
34430             // abort inflight requests that are no longer needed
34431             var cache = _mlyCache[which];
34432             Object.keys(cache.inflight).forEach(function(k) {
34433                 var wanted = tiles.find(function(tile) { return k.indexOf(tile.id + ',') === 0; });
34434                 if (!wanted) {
34435                     abortRequest$3(cache.inflight[k]);
34436                     delete cache.inflight[k];
34437                 }
34438             });
34439
34440             tiles.forEach(function(tile) {
34441                 loadNextTilePage(which, currZoom, url, tile);
34442             });
34443         }
34444
34445
34446         function loadNextTilePage(which, currZoom, url, tile) {
34447             var cache = _mlyCache[which];
34448             var rect = tile.extent.rectangle();
34449             var maxPages = maxPageAtZoom(currZoom);
34450             var nextPage = cache.nextPage[tile.id] || 0;
34451             var nextURL = cache.nextURL[tile.id] || url +
34452                 utilQsString({
34453                     per_page: maxResults,
34454                     page: nextPage,
34455                     client_id: clientId,
34456                     bbox: [rect[0], rect[1], rect[2], rect[3]].join(','),
34457                 });
34458
34459             if (nextPage > maxPages) { return; }
34460
34461             var id = tile.id + ',' + String(nextPage);
34462             if (cache.loaded[id] || cache.inflight[id]) { return; }
34463
34464             var controller = new AbortController();
34465             cache.inflight[id] = controller;
34466
34467             var options = {
34468                 method: 'GET',
34469                 signal: controller.signal,
34470                 headers: { 'Content-Type': 'application/json' }
34471             };
34472
34473             fetch(nextURL, options)
34474                 .then(function(response) {
34475                     if (!response.ok) {
34476                         throw new Error(response.status + ' ' + response.statusText);
34477                     }
34478                     var linkHeader = response.headers.get('Link');
34479                     if (linkHeader) {
34480                         var pagination = parsePagination(linkHeader);
34481                         if (pagination.next) {
34482                             cache.nextURL[tile.id] = pagination.next;
34483                         }
34484                     }
34485                     return response.json();
34486                 })
34487                 .then(function(data) {
34488                     cache.loaded[id] = true;
34489                     delete cache.inflight[id];
34490                     if (!data || !data.features || !data.features.length) {
34491                         throw new Error('No Data');
34492                     }
34493
34494                     var features = data.features.map(function(feature) {
34495                         var loc = feature.geometry.coordinates;
34496                         var d;
34497
34498                         // An image (shown as a green dot on the map) is a single street photo with extra
34499                         // information such as location, camera angle (CA), camera model, and so on.
34500                         // Each image feature is a GeoJSON Point
34501                         if (which === 'images') {
34502                             d = {
34503                                 loc: loc,
34504                                 key: feature.properties.key,
34505                                 ca: feature.properties.ca,
34506                                 captured_at: feature.properties.captured_at,
34507                                 captured_by: feature.properties.username,
34508                                 pano: feature.properties.pano
34509                             };
34510
34511                             cache.forImageKey[d.key] = d;     // cache imageKey -> image
34512
34513                         // Mapillary organizes images as sequences. A sequence of images are continuously captured
34514                         // by a user at a give time. Sequences are shown on the map as green lines.
34515                         // Each sequence feature is a GeoJSON LineString
34516                         } else if (which === 'sequences') {
34517                             var sequenceKey = feature.properties.key;
34518                             cache.lineString[sequenceKey] = feature;           // cache sequenceKey -> lineString
34519                             feature.properties.coordinateProperties.image_keys.forEach(function(imageKey) {
34520                                 cache.forImageKey[imageKey] = sequenceKey;     // cache imageKey -> sequenceKey
34521                             });
34522                             return false;    // because no `d` data worth loading into an rbush
34523
34524                         // An image detection is a semantic pixel area on an image. The area could indicate
34525                         // sky, trees, sidewalk in the image. A detection can be a polygon, a bounding box, or a point.
34526                         // Each image_detection feature is a GeoJSON Point (located where the image was taken)
34527                         } else if (which === 'image_detections') {
34528                             d = {
34529                                 key: feature.properties.key,
34530                                 image_key: feature.properties.image_key,
34531                                 value: feature.properties.value,
34532                                 package: feature.properties.package,
34533                                 shape: feature.properties.shape
34534                             };
34535
34536                             // cache imageKey -> image_detections
34537                             if (!cache.forImageKey[d.image_key]) {
34538                                 cache.forImageKey[d.image_key] = [];
34539                             }
34540                             cache.forImageKey[d.image_key].push(d);
34541                             return false;    // because no `d` data worth loading into an rbush
34542
34543
34544                         // A map feature is a real world object that can be shown on a map. It could be any object
34545                         // recognized from images, manually added in images, or added on the map.
34546                         // Each map feature is a GeoJSON Point (located where the feature is)
34547                         } else if (which === 'map_features' || which === 'points') {
34548                             d = {
34549                                 loc: loc,
34550                                 key: feature.properties.key,
34551                                 value: feature.properties.value,
34552                                 package: feature.properties.package,
34553                                 detections: feature.properties.detections
34554                             };
34555                         }
34556
34557                         return {
34558                             minX: loc[0], minY: loc[1], maxX: loc[0], maxY: loc[1], data: d
34559                         };
34560
34561                     }).filter(Boolean);
34562
34563                     if (cache.rtree && features) {
34564                         cache.rtree.load(features);
34565                     }
34566
34567                     if (data.features.length === maxResults) {  // more pages to load
34568                         cache.nextPage[tile.id] = nextPage + 1;
34569                         loadNextTilePage(which, currZoom, url, tile);
34570                     } else {
34571                         cache.nextPage[tile.id] = Infinity;     // no more pages to load
34572                     }
34573
34574                     if (which === 'images' || which === 'sequences') {
34575                         dispatch$4.call('loadedImages');
34576                     } else if (which === 'map_features') {
34577                         dispatch$4.call('loadedSigns');
34578                     } else if (which === 'points') {
34579                         dispatch$4.call('loadedMapFeatures');
34580                     }
34581                 })
34582                 .catch(function() {
34583                     cache.loaded[id] = true;
34584                     delete cache.inflight[id];
34585                 });
34586         }
34587
34588         // extract links to pages of API results
34589         function parsePagination(links) {
34590             return links.split(',').map(function(rel) {
34591                 var elements = rel.split(';');
34592                 if (elements.length === 2) {
34593                     return [
34594                         /<(.+)>/.exec(elements[0])[1],
34595                         /rel="(.+)"/.exec(elements[1])[1]
34596                     ];
34597                 } else {
34598                     return ['',''];
34599                 }
34600             }).reduce(function(pagination, val) {
34601                 pagination[val[1]] = val[0];
34602                 return pagination;
34603             }, {});
34604         }
34605
34606
34607         // partition viewport into higher zoom tiles
34608         function partitionViewport(projection) {
34609             var z = geoScaleToZoom(projection.scale());
34610             var z2 = (Math.ceil(z * 2) / 2) + 2.5;   // round to next 0.5 and add 2.5
34611             var tiler = utilTiler().zoomExtent([z2, z2]);
34612
34613             return tiler.getTiles(projection)
34614                 .map(function(tile) { return tile.extent; });
34615         }
34616
34617
34618         // no more than `limit` results per partition.
34619         function searchLimited(limit, projection, rtree) {
34620             limit = limit || 5;
34621
34622             return partitionViewport(projection)
34623                 .reduce(function(result, extent) {
34624                     var found = rtree.search(extent.bbox())
34625                         .slice(0, limit)
34626                         .map(function(d) { return d.data; });
34627
34628                     return (found.length ? result.concat(found) : result);
34629                 }, []);
34630         }
34631
34632
34633
34634         var serviceMapillary = {
34635
34636             init: function() {
34637                 if (!_mlyCache) {
34638                     this.reset();
34639                 }
34640
34641                 this.event = utilRebind(this, dispatch$4, 'on');
34642             },
34643
34644             reset: function() {
34645                 if (_mlyCache) {
34646                     Object.values(_mlyCache.images.inflight).forEach(abortRequest$3);
34647                     Object.values(_mlyCache.image_detections.inflight).forEach(abortRequest$3);
34648                     Object.values(_mlyCache.map_features.inflight).forEach(abortRequest$3);
34649                     Object.values(_mlyCache.points.inflight).forEach(abortRequest$3);
34650                     Object.values(_mlyCache.sequences.inflight).forEach(abortRequest$3);
34651                 }
34652
34653                 _mlyCache = {
34654                     images: { inflight: {}, loaded: {}, nextPage: {}, nextURL: {}, rtree: new RBush(), forImageKey: {} },
34655                     image_detections: { inflight: {}, loaded: {}, nextPage: {}, nextURL: {}, forImageKey: {} },
34656                     map_features: { inflight: {}, loaded: {}, nextPage: {}, nextURL: {}, rtree: new RBush() },
34657                     points: { inflight: {}, loaded: {}, nextPage: {}, nextURL: {}, rtree: new RBush() },
34658                     sequences: { inflight: {}, loaded: {}, nextPage: {}, nextURL: {}, rtree: new RBush(), forImageKey: {}, lineString: {} }
34659                 };
34660
34661                 _mlySelectedImageKey = null;
34662                 _mlyClicks = [];
34663             },
34664
34665
34666             images: function(projection) {
34667                 var limit = 5;
34668                 return searchLimited(limit, projection, _mlyCache.images.rtree);
34669             },
34670
34671
34672             signs: function(projection) {
34673                 var limit = 5;
34674                 return searchLimited(limit, projection, _mlyCache.map_features.rtree);
34675             },
34676
34677
34678             mapFeatures: function(projection) {
34679                 var limit = 5;
34680                 return searchLimited(limit, projection, _mlyCache.points.rtree);
34681             },
34682
34683
34684             cachedImage: function(imageKey) {
34685                 return _mlyCache.images.forImageKey[imageKey];
34686             },
34687
34688
34689             sequences: function(projection) {
34690                 var viewport = projection.clipExtent();
34691                 var min = [viewport[0][0], viewport[1][1]];
34692                 var max = [viewport[1][0], viewport[0][1]];
34693                 var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
34694                 var sequenceKeys = {};
34695
34696                 // all sequences for images in viewport
34697                 _mlyCache.images.rtree.search(bbox)
34698                     .forEach(function(d) {
34699                         var sequenceKey = _mlyCache.sequences.forImageKey[d.data.key];
34700                         if (sequenceKey) {
34701                             sequenceKeys[sequenceKey] = true;
34702                         }
34703                     });
34704
34705                 // Return lineStrings for the sequences
34706                 return Object.keys(sequenceKeys).map(function(sequenceKey) {
34707                     return _mlyCache.sequences.lineString[sequenceKey];
34708                 });
34709             },
34710
34711
34712             signsSupported: function() {
34713                 return true;
34714             },
34715
34716
34717             loadImages: function(projection) {
34718                 loadTiles('images', apibase + 'images?sort_by=key&', projection);
34719                 loadTiles('sequences', apibase + 'sequences?sort_by=key&', projection);
34720             },
34721
34722
34723             loadSigns: function(projection) {
34724                 // if we are looking at signs, we'll actually need to fetch images too
34725                 loadTiles('images', apibase + 'images?sort_by=key&', projection);
34726                 loadTiles('map_features', apibase + 'map_features?layers=trafficsigns&min_nbr_image_detections=2&sort_by=key&', projection);
34727                 loadTiles('image_detections', apibase + 'image_detections?layers=trafficsigns&sort_by=key&', projection);
34728             },
34729
34730
34731             loadMapFeatures: function(projection) {
34732                 // if we are looking at signs, we'll actually need to fetch images too
34733                 loadTiles('images', apibase + 'images?sort_by=key', projection);
34734                 loadTiles('points', apibase + 'map_features?layers=points&min_nbr_image_detections=2&sort_by=key&values=' + mapFeatureConfig.values + '&', projection);
34735                 loadTiles('image_detections', apibase + 'image_detections?layers=points&sort_by=key&values=' + mapFeatureConfig.values + '&', projection);
34736             },
34737
34738
34739             loadViewer: function(context) {
34740                 // add mly-wrapper
34741                 var wrap = context.container().select('.photoviewer')
34742                     .selectAll('.mly-wrapper')
34743                     .data([0]);
34744
34745                 wrap.enter()
34746                     .append('div')
34747                     .attr('id', 'ideditor-mly')
34748                     .attr('class', 'photo-wrapper mly-wrapper')
34749                     .classed('hide', true);
34750
34751                 // load mapillary-viewercss
34752                 select('head').selectAll('#ideditor-mapillary-viewercss')
34753                     .data([0])
34754                     .enter()
34755                     .append('link')
34756                     .attr('id', 'ideditor-mapillary-viewercss')
34757                     .attr('rel', 'stylesheet')
34758                     .attr('href', context.asset(viewercss));
34759
34760                 // load mapillary-viewerjs
34761                 select('head').selectAll('#ideditor-mapillary-viewerjs')
34762                     .data([0])
34763                     .enter()
34764                     .append('script')
34765                     .attr('id', 'ideditor-mapillary-viewerjs')
34766                     .attr('src', context.asset(viewerjs));
34767
34768                 // load mapillary signs sprite
34769                 var defs = context.container().select('defs');
34770                 defs.call(svgDefs(context).addSprites, ['mapillary-sprite', 'mapillary-object-sprite'], false /* don't override colors */ );
34771
34772                 // Register viewer resize handler
34773                 context.ui().photoviewer.on('resize.mapillary', function() {
34774                     if (_mlyViewer) {
34775                         _mlyViewer.resize();
34776                     }
34777                 });
34778             },
34779
34780
34781             showViewer: function(context) {
34782                 var wrap = context.container().select('.photoviewer')
34783                     .classed('hide', false);
34784
34785                 var isHidden = wrap.selectAll('.photo-wrapper.mly-wrapper.hide').size();
34786
34787                 if (isHidden && _mlyViewer) {
34788                     wrap
34789                         .selectAll('.photo-wrapper:not(.mly-wrapper)')
34790                         .classed('hide', true);
34791
34792                     wrap
34793                         .selectAll('.photo-wrapper.mly-wrapper')
34794                         .classed('hide', false);
34795
34796                     _mlyViewer.resize();
34797                 }
34798
34799                 return this;
34800             },
34801
34802
34803             hideViewer: function(context) {
34804                 _mlySelectedImageKey = null;
34805
34806                 if (!_mlyFallback && _mlyViewer) {
34807                     _mlyViewer.getComponent('sequence').stop();
34808                 }
34809
34810                 var viewer = context.container().select('.photoviewer');
34811                 if (!viewer.empty()) { viewer.datum(null); }
34812
34813                 viewer
34814                     .classed('hide', true)
34815                     .selectAll('.photo-wrapper')
34816                     .classed('hide', true);
34817
34818                 context.container().selectAll('.viewfield-group, .sequence, .icon-detected')
34819                     .classed('currentView', false);
34820
34821                 return this.setStyles(context, null, true);
34822             },
34823
34824
34825             parsePagination: parsePagination,
34826
34827
34828             updateViewer: function(context, imageKey) {
34829                 if (!imageKey) { return this; }
34830
34831                 if (!_mlyViewer) {
34832                     this.initViewer(context, imageKey);
34833                 } else {
34834                     _mlyViewer.moveToKey(imageKey)
34835                         .catch(function(e) { console.error('mly3', e); });  // eslint-disable-line no-console
34836                 }
34837
34838                 return this;
34839             },
34840
34841
34842             initViewer: function(context, imageKey) {
34843                 var that = this;
34844                 if (window.Mapillary && imageKey) {
34845                     var opts = {
34846                         baseImageSize: 320,
34847                         component: {
34848                             cover: false,
34849                             keyboard: false,
34850                             tag: true
34851                         }
34852                     };
34853
34854                     // Disable components requiring WebGL support
34855                     if (!Mapillary.isSupported() && Mapillary.isFallbackSupported()) {
34856                         _mlyFallback = true;
34857                         opts.component = {
34858                             cover: false,
34859                             direction: false,
34860                             imagePlane: false,
34861                             keyboard: false,
34862                             mouse: false,
34863                             sequence: false,
34864                             tag: false,
34865                             image: true,        // fallback
34866                             navigation: true    // fallback
34867                         };
34868                     }
34869
34870                     _mlyViewer = new Mapillary.Viewer('ideditor-mly', clientId, null, opts);
34871                     _mlyViewer.on('nodechanged', nodeChanged);
34872                     _mlyViewer.on('bearingchanged', bearingChanged);
34873                     _mlyViewer.moveToKey(imageKey)
34874                         .catch(function(e) { console.error('mly3', e); });  // eslint-disable-line no-console
34875                 }
34876
34877                 // nodeChanged: called after the viewer has changed images and is ready.
34878                 //
34879                 // There is some logic here to batch up clicks into a _mlyClicks array
34880                 // because the user might click on a lot of markers quickly and nodechanged
34881                 // may be called out of order asychronously.
34882                 //
34883                 // Clicks are added to the array in `selectedImage` and removed here.
34884                 //
34885                 function nodeChanged(node) {
34886                     if (!_mlyFallback) {
34887                         _mlyViewer.getComponent('tag').removeAll();  // remove previous detections
34888                     }
34889
34890                     var clicks = _mlyClicks;
34891                     var index = clicks.indexOf(node.key);
34892                     var selectedKey = _mlySelectedImageKey;
34893
34894                     if (index > -1) {              // `nodechanged` initiated from clicking on a marker..
34895                         clicks.splice(index, 1);   // remove the click
34896                         // If `node.key` matches the current _mlySelectedImageKey, call `selectImage()`
34897                         // one more time to update the detections and attribution..
34898                         if (node.key === selectedKey) {
34899                             that.selectImage(context, _mlySelectedImageKey, true);
34900                         }
34901                     } else {             // `nodechanged` initiated from the Mapillary viewer controls..
34902                         var loc = node.computedLatLon ? [node.computedLatLon.lon, node.computedLatLon.lat] : [node.latLon.lon, node.latLon.lat];
34903                         context.map().centerEase(loc);
34904                         that.selectImage(context, node.key, true);
34905                     }
34906                 }
34907
34908                 function bearingChanged(e) {
34909                     dispatch$4.call('bearingChanged', undefined, e);
34910                 }
34911             },
34912
34913
34914             // Pass in the image key string as `imageKey`.
34915             // This allows images to be selected from places that dont have access
34916             // to the full image datum (like the street signs layer or the js viewer)
34917             selectImage: function(context, imageKey, fromViewer) {
34918
34919                 _mlySelectedImageKey = imageKey;
34920
34921                 // Note the datum could be missing, but we'll try to carry on anyway.
34922                 // There just might be a delay before user sees detections, captured_at, etc.
34923                 var d = _mlyCache.images.forImageKey[imageKey];
34924
34925                 var viewer = context.container().select('.photoviewer');
34926                 if (!viewer.empty()) { viewer.datum(d); }
34927
34928                 imageKey = (d && d.key) || imageKey;
34929                 if (!fromViewer && imageKey) {
34930                     _mlyClicks.push(imageKey);
34931                 }
34932
34933                 this.setStyles(context, null, true);
34934
34935                 // if signs signs are shown, highlight the ones that appear in this image
34936                 context.container().selectAll('.layer-mapillary-signs .icon-detected')
34937                     .classed('currentView', function(d) {
34938                         return d.detections.some(function(detection) {
34939                             return detection.image_key === imageKey;
34940                         });
34941                     });
34942
34943                 if (d) {
34944                     this.updateDetections(d);
34945                 }
34946
34947                 return this;
34948             },
34949
34950
34951             getSelectedImageKey: function() {
34952                 return _mlySelectedImageKey;
34953             },
34954
34955
34956             getSequenceKeyForImageKey: function(imageKey) {
34957                 return _mlyCache.sequences.forImageKey[imageKey];
34958             },
34959
34960
34961             // Updates the currently highlighted sequence and selected bubble.
34962             // Reset is only necessary when interacting with the viewport because
34963             // this implicitly changes the currently selected bubble/sequence
34964             setStyles: function(context, hovered, reset) {
34965                 if (reset) {  // reset all layers
34966                     context.container().selectAll('.viewfield-group')
34967                         .classed('highlighted', false)
34968                         .classed('hovered', false)
34969                         .classed('currentView', false);
34970
34971                     context.container().selectAll('.sequence')
34972                         .classed('highlighted', false)
34973                         .classed('currentView', false);
34974                 }
34975
34976                 var hoveredImageKey = hovered && hovered.key;
34977                 var hoveredSequenceKey = hoveredImageKey && this.getSequenceKeyForImageKey(hoveredImageKey);
34978                 var hoveredLineString = hoveredSequenceKey && _mlyCache.sequences.lineString[hoveredSequenceKey];
34979                 var hoveredImageKeys = (hoveredLineString && hoveredLineString.properties.coordinateProperties.image_keys) || [];
34980
34981                 var selectedImageKey = _mlySelectedImageKey;
34982                 var selectedSequenceKey = selectedImageKey && this.getSequenceKeyForImageKey(selectedImageKey);
34983                 var selectedLineString = selectedSequenceKey && _mlyCache.sequences.lineString[selectedSequenceKey];
34984                 var selectedImageKeys = (selectedLineString && selectedLineString.properties.coordinateProperties.image_keys) || [];
34985
34986                 // highlight sibling viewfields on either the selected or the hovered sequences
34987                 var highlightedImageKeys = utilArrayUnion(hoveredImageKeys, selectedImageKeys);
34988
34989                 context.container().selectAll('.layer-mapillary .viewfield-group')
34990                     .classed('highlighted', function(d) { return highlightedImageKeys.indexOf(d.key) !== -1; })
34991                     .classed('hovered', function(d) { return d.key === hoveredImageKey; })
34992                     .classed('currentView', function(d) { return d.key === selectedImageKey; });
34993
34994                 context.container().selectAll('.layer-mapillary .sequence')
34995                     .classed('highlighted', function(d) { return d.properties.key === hoveredSequenceKey; })
34996                     .classed('currentView', function(d) { return d.properties.key === selectedSequenceKey; });
34997
34998                 // update viewfields if needed
34999                 context.container().selectAll('.viewfield-group .viewfield')
35000                     .attr('d', viewfieldPath);
35001
35002                 function viewfieldPath() {
35003                     var d = this.parentNode.__data__;
35004                     if (d.pano && d.key !== selectedImageKey) {
35005                         return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0';
35006                     } else {
35007                         return 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z';
35008                     }
35009                 }
35010
35011                 return this;
35012             },
35013
35014
35015             updateDetections: function(d) {
35016                 if (!_mlyViewer || _mlyFallback) { return; }
35017
35018                 var imageKey = d && d.key;
35019                 if (!imageKey) { return; }
35020
35021                 var detections = _mlyCache.image_detections.forImageKey[imageKey] || [];
35022                 detections.forEach(function(data) {
35023                     var tag = makeTag(data);
35024                     if (tag) {
35025                         var tagComponent = _mlyViewer.getComponent('tag');
35026                         tagComponent.add([tag]);
35027                     }
35028                 });
35029
35030                 function makeTag(data) {
35031                     var valueParts = data.value.split('--');
35032                     if (valueParts.length !== 3) { return; }
35033
35034                     var text = valueParts[1].replace(/-/g, ' ');
35035                     var tag;
35036
35037                     // Currently only two shapes <Polygon|Point>
35038                     if (data.shape.type === 'Polygon') {
35039                         var polygonGeometry = new Mapillary
35040                             .TagComponent
35041                             .PolygonGeometry(data.shape.coordinates[0]);
35042
35043                         tag = new Mapillary.TagComponent.OutlineTag(
35044                             data.key,
35045                             polygonGeometry,
35046                             {
35047                                 text: text,
35048                                 textColor: 0xffff00,
35049                                 lineColor: 0xffff00,
35050                                 lineWidth: 2,
35051                                 fillColor: 0xffff00,
35052                                 fillOpacity: 0.3,
35053                             }
35054                         );
35055
35056                     } else if (data.shape.type === 'Point') {
35057                         var pointGeometry = new Mapillary
35058                             .TagComponent
35059                             .PointGeometry(data.shape.coordinates[0]);
35060
35061                         tag = new Mapillary.TagComponent.SpotTag(
35062                             data.key,
35063                             pointGeometry,
35064                             {
35065                                 text: text,
35066                                 color: 0xffff00,
35067                                 textColor: 0xffff00
35068                             }
35069                         );
35070                     }
35071
35072                     return tag;
35073                 }
35074             },
35075
35076
35077             cache: function() {
35078                 return _mlyCache;
35079             }
35080
35081         };
35082
35083         function validationIssue(attrs) {
35084             this.type = attrs.type;                // required - name of rule that created the issue (e.g. 'missing_tag')
35085             this.subtype = attrs.subtype;          // optional - category of the issue within the type (e.g. 'relation_type' under 'missing_tag')
35086             this.severity = attrs.severity;        // required - 'warning' or 'error'
35087             this.message = attrs.message;          // required - function returning localized string
35088             this.reference = attrs.reference;      // optional - function(selection) to render reference information
35089             this.entityIds = attrs.entityIds;      // optional - array of IDs of entities involved in the issue
35090             this.loc = attrs.loc;                  // optional - [lon, lat] to zoom in on to see the issue
35091             this.data = attrs.data;                // optional - object containing extra data for the fixes
35092             this.dynamicFixes = attrs.dynamicFixes;// optional - function(context) returning fixes
35093             this.hash = attrs.hash;                // optional - string to further differentiate the issue
35094
35095             this.id = generateID.apply(this);      // generated - see below
35096             this.autoFix = null;                   // generated - if autofix exists, will be set below
35097
35098             // A unique, deterministic string hash.
35099             // Issues with identical id values are considered identical.
35100             function generateID() {
35101                 var parts = [this.type];
35102
35103                 if (this.hash) {   // subclasses can pass in their own differentiator
35104                     parts.push(this.hash);
35105                 }
35106
35107                 if (this.subtype) {
35108                     parts.push(this.subtype);
35109                 }
35110
35111                 // include the entities this issue is for
35112                 // (sort them so the id is deterministic)
35113                 if (this.entityIds) {
35114                     var entityKeys = this.entityIds.slice().sort();
35115                     parts.push.apply(parts, entityKeys);
35116                 }
35117
35118                 return parts.join(':');
35119             }
35120
35121             this.extent = function(resolver) {
35122                 if (this.loc) {
35123                     return geoExtent(this.loc);
35124                 }
35125                 if (this.entityIds && this.entityIds.length) {
35126                     return this.entityIds.reduce(function(extent, entityId) {
35127                         return extent.extend(resolver.entity(entityId).extent(resolver));
35128                     }, geoExtent());
35129                 }
35130                 return null;
35131             };
35132
35133             this.fixes = function(context) {
35134                 var fixes = this.dynamicFixes ? this.dynamicFixes(context) : [];
35135                 var issue = this;
35136
35137                 if (issue.severity === 'warning') {
35138                     // allow ignoring any issue that's not an error
35139                     fixes.push(new validationIssueFix({
35140                         title: _t('issues.fix.ignore_issue.title'),
35141                         icon: 'iD-icon-close',
35142                         onClick: function() {
35143                             context.validator().ignoreIssue(this.issue.id);
35144                         }
35145                     }));
35146                 }
35147
35148                 fixes.forEach(function(fix) {
35149                     fix.id = fix.title;
35150                     // add a reference to the issue for use in actions
35151                     fix.issue = issue;
35152                     if (fix.autoArgs) {
35153                         issue.autoFix = fix;
35154                     }
35155                 });
35156                 return fixes;
35157             };
35158
35159         }
35160
35161
35162         function validationIssueFix(attrs) {
35163             this.title = attrs.title;                   // Required
35164             this.onClick = attrs.onClick;               // Optional - the function to run to apply the fix
35165             this.disabledReason = attrs.disabledReason; // Optional - a string explaining why the fix is unavailable, if any
35166             this.icon = attrs.icon;                     // Optional - shows 'iD-icon-wrench' if not set
35167             this.entityIds = attrs.entityIds || [];     // Optional - used for hover-higlighting.
35168             this.autoArgs = attrs.autoArgs;             // Optional - pass [actions, annotation] arglist if this fix can automatically run
35169
35170             this.issue = null;    // Generated link - added by validationIssue
35171         }
35172
35173         var buildRuleChecks = function() {
35174             return {
35175                 equals: function (equals) {
35176                     return function(tags) {
35177                         return Object.keys(equals).every(function(k) {
35178                             return equals[k] === tags[k];
35179                         });
35180                     };
35181                 },
35182                 notEquals: function (notEquals) {
35183                     return function(tags) {
35184                         return Object.keys(notEquals).some(function(k) {
35185                             return notEquals[k] !== tags[k];
35186                         });
35187                     };
35188                 },
35189                 absence: function(absence) {
35190                     return function(tags) {
35191                         return Object.keys(tags).indexOf(absence) === -1;
35192                     };
35193                 },
35194                 presence: function(presence) {
35195                     return function(tags) {
35196                         return Object.keys(tags).indexOf(presence) > -1;
35197                     };
35198                 },
35199                 greaterThan: function(greaterThan) {
35200                     var key = Object.keys(greaterThan)[0];
35201                     var value = greaterThan[key];
35202
35203                     return function(tags) {
35204                         return tags[key] > value;
35205                     };
35206                 },
35207                 greaterThanEqual: function(greaterThanEqual) {
35208                     var key = Object.keys(greaterThanEqual)[0];
35209                     var value = greaterThanEqual[key];
35210
35211                     return function(tags) {
35212                         return tags[key] >= value;
35213                     };
35214                 },
35215                 lessThan: function(lessThan) {
35216                     var key = Object.keys(lessThan)[0];
35217                     var value = lessThan[key];
35218
35219                     return function(tags) {
35220                         return tags[key] < value;
35221                     };
35222                 },
35223                 lessThanEqual: function(lessThanEqual) {
35224                     var key = Object.keys(lessThanEqual)[0];
35225                     var value = lessThanEqual[key];
35226
35227                     return function(tags) {
35228                         return tags[key] <= value;
35229                     };
35230                 },
35231                 positiveRegex: function(positiveRegex) {
35232                     var tagKey = Object.keys(positiveRegex)[0];
35233                     var expression = positiveRegex[tagKey].join('|');
35234                     var regex = new RegExp(expression);
35235
35236                     return function(tags) {
35237                         return regex.test(tags[tagKey]);
35238                     };
35239                 },
35240                 negativeRegex: function(negativeRegex) {
35241                     var tagKey = Object.keys(negativeRegex)[0];
35242                     var expression = negativeRegex[tagKey].join('|');
35243                     var regex = new RegExp(expression);
35244
35245                     return function(tags) {
35246                         return !regex.test(tags[tagKey]);
35247                     };
35248                 }
35249             };
35250         };
35251
35252         var buildLineKeys = function() {
35253             return {
35254                 highway: {
35255                     rest_area: true,
35256                     services: true
35257                 },
35258                 railway: {
35259                     roundhouse: true,
35260                     station: true,
35261                     traverser: true,
35262                     turntable: true,
35263                     wash: true
35264                 }
35265             };
35266         };
35267
35268         var serviceMapRules = {
35269             init: function() {
35270                 this._ruleChecks  = buildRuleChecks();
35271                 this._validationRules = [];
35272                 this._areaKeys = osmAreaKeys;
35273                 this._lineKeys = buildLineKeys();
35274             },
35275
35276             // list of rules only relevant to tag checks...
35277             filterRuleChecks: function(selector) {
35278                 var _ruleChecks = this._ruleChecks;
35279                 return Object.keys(selector).reduce(function(rules, key) {
35280                     if (['geometry', 'error', 'warning'].indexOf(key) === -1) {
35281                         rules.push(_ruleChecks[key](selector[key]));
35282                     }
35283                     return rules;
35284                 }, []);
35285             },
35286
35287             // builds tagMap from mapcss-parse selector object...
35288             buildTagMap: function(selector) {
35289                 var getRegexValues = function(regexes) {
35290                     return regexes.map(function(regex) {
35291                         return regex.replace(/\$|\^/g, '');
35292                     });
35293                 };
35294
35295                 var tagMap = Object.keys(selector).reduce(function (expectedTags, key) {
35296                     var values;
35297                     var isRegex = /regex/gi.test(key);
35298                     var isEqual = /equals/gi.test(key);
35299
35300                     if (isRegex || isEqual) {
35301                         Object.keys(selector[key]).forEach(function(selectorKey) {
35302                             values = isEqual ? [selector[key][selectorKey]] : getRegexValues(selector[key][selectorKey]);
35303
35304                             if (expectedTags.hasOwnProperty(selectorKey)) {
35305                                 values = values.concat(expectedTags[selectorKey]);
35306                             }
35307
35308                             expectedTags[selectorKey] = values;
35309                         });
35310
35311                     } else if (/(greater|less)Than(Equal)?|presence/g.test(key)) {
35312                         var tagKey = /presence/.test(key) ? selector[key] : Object.keys(selector[key])[0];
35313
35314                         values = [selector[key][tagKey]];
35315
35316                         if (expectedTags.hasOwnProperty(tagKey)) {
35317                             values = values.concat(expectedTags[tagKey]);
35318                         }
35319
35320                         expectedTags[tagKey] = values;
35321                     }
35322
35323                     return expectedTags;
35324                 }, {});
35325
35326                 return tagMap;
35327             },
35328
35329             // inspired by osmWay#isArea()
35330             inferGeometry: function(tagMap) {
35331                 var _lineKeys = this._lineKeys;
35332                 var _areaKeys = this._areaKeys;
35333
35334                 var keyValueDoesNotImplyArea = function(key) {
35335                     return utilArrayIntersection(tagMap[key], Object.keys(_areaKeys[key])).length > 0;
35336                 };
35337                 var keyValueImpliesLine = function(key) {
35338                     return utilArrayIntersection(tagMap[key], Object.keys(_lineKeys[key])).length > 0;
35339                 };
35340
35341                 if (tagMap.hasOwnProperty('area')) {
35342                     if (tagMap.area.indexOf('yes') > -1) {
35343                         return 'area';
35344                     }
35345                     if (tagMap.area.indexOf('no') > -1) {
35346                         return 'line';
35347                     }
35348                 }
35349
35350                 for (var key in tagMap) {
35351                     if (key in _areaKeys && !keyValueDoesNotImplyArea(key)) {
35352                         return 'area';
35353                     }
35354                     if (key in _lineKeys && keyValueImpliesLine(key)) {
35355                         return 'area';
35356                     }
35357                 }
35358
35359                 return 'line';
35360             },
35361
35362             // adds from mapcss-parse selector check...
35363             addRule: function(selector) {
35364                 var rule = {
35365                     // checks relevant to mapcss-selector
35366                     checks: this.filterRuleChecks(selector),
35367                     // true if all conditions for a tag error are true..
35368                     matches: function(entity) {
35369                         return this.checks.every(function(check) {
35370                             return check(entity.tags);
35371                         });
35372                     },
35373                     // borrowed from Way#isArea()
35374                     inferredGeometry: this.inferGeometry(this.buildTagMap(selector), this._areaKeys),
35375                     geometryMatches: function(entity, graph) {
35376                         if (entity.type === 'node' || entity.type === 'relation') {
35377                             return selector.geometry === entity.type;
35378                         } else if (entity.type === 'way') {
35379                             return this.inferredGeometry === entity.geometry(graph);
35380                         }
35381                     },
35382                     // when geometries match and tag matches are present, return a warning...
35383                     findIssues: function (entity, graph, issues) {
35384                         if (this.geometryMatches(entity, graph) && this.matches(entity)) {
35385                             var severity = Object.keys(selector).indexOf('error') > -1
35386                                     ? 'error'
35387                                     : 'warning';
35388                             var message = selector[severity];
35389                             issues.push(new validationIssue({
35390                                 type: 'maprules',
35391                                 severity: severity,
35392                                 message: function() {
35393                                     return message;
35394                                 },
35395                                 entityIds: [entity.id]
35396                             }));
35397                         }
35398                     }
35399                 };
35400                 this._validationRules.push(rule);
35401             },
35402
35403             clearRules: function() { this._validationRules = []; },
35404
35405             // returns validationRules...
35406             validationRules: function() { return this._validationRules; },
35407
35408             // returns ruleChecks
35409             ruleChecks: function() { return this._ruleChecks; }
35410         };
35411
35412         var apibase$1 = 'https://nominatim.openstreetmap.org/';
35413         var _inflight = {};
35414         var _nominatimCache;
35415
35416
35417         var serviceNominatim = {
35418
35419             init: function() {
35420                 _inflight = {};
35421                 _nominatimCache = new RBush();
35422             },
35423
35424             reset: function() {
35425                 Object.values(_inflight).forEach(function(controller) { controller.abort(); });
35426                 _inflight = {};
35427                 _nominatimCache = new RBush();
35428             },
35429
35430
35431             countryCode: function (location, callback) {
35432                 this.reverse(location, function(err, result) {
35433                     if (err) {
35434                         return callback(err);
35435                     } else if (result.address) {
35436                         return callback(null, result.address.country_code);
35437                     } else {
35438                         return callback('Unable to geocode', null);
35439                     }
35440                 });
35441             },
35442
35443
35444             reverse: function (loc, callback) {
35445                 var cached = _nominatimCache.search(
35446                     { minX: loc[0], minY: loc[1], maxX: loc[0], maxY: loc[1] }
35447                 );
35448
35449                 if (cached.length > 0) {
35450                     if (callback) { callback(null, cached[0].data); }
35451                     return;
35452                 }
35453
35454                 var params = { zoom: 13, format: 'json', addressdetails: 1, lat: loc[1], lon: loc[0] };
35455                 var url = apibase$1 + 'reverse?' + utilQsString(params);
35456
35457                 if (_inflight[url]) { return; }
35458                 var controller = new AbortController();
35459                 _inflight[url] = controller;
35460
35461                 d3_json(url, { signal: controller.signal })
35462                     .then(function(result) {
35463                         delete _inflight[url];
35464                         if (result && result.error) {
35465                             throw new Error(result.error);
35466                         }
35467                         var extent = geoExtent(loc).padByMeters(200);
35468                         _nominatimCache.insert(Object.assign(extent.bbox(), {data: result}));
35469                         if (callback) { callback(null, result); }
35470                     })
35471                     .catch(function(err) {
35472                         delete _inflight[url];
35473                         if (err.name === 'AbortError') { return; }
35474                         if (callback) { callback(err.message); }
35475                     });
35476             },
35477
35478
35479             search: function (val, callback) {
35480                 var searchVal = encodeURIComponent(val);
35481                 var url = apibase$1 + 'search/' + searchVal + '?limit=10&format=json';
35482
35483                 if (_inflight[url]) { return; }
35484                 var controller = new AbortController();
35485                 _inflight[url] = controller;
35486
35487                 d3_json(url, { signal: controller.signal })
35488                     .then(function(result) {
35489                         delete _inflight[url];
35490                         if (result && result.error) {
35491                             throw new Error(result.error);
35492                         }
35493                         if (callback) { callback(null, result); }
35494                     })
35495                     .catch(function(err) {
35496                         delete _inflight[url];
35497                         if (err.name === 'AbortError') { return; }
35498                         if (callback) { callback(err.message); }
35499                     });
35500             }
35501
35502         };
35503
35504         var apibase$2 = 'https://openstreetcam.org';
35505         var maxResults$1 = 1000;
35506         var tileZoom$1 = 14;
35507         var tiler$4 = utilTiler().zoomExtent([tileZoom$1, tileZoom$1]).skipNullIsland(true);
35508         var dispatch$5 = dispatch('loadedImages');
35509         var imgZoom = d3_zoom()
35510             .extent([[0, 0], [320, 240]])
35511             .translateExtent([[0, 0], [320, 240]])
35512             .scaleExtent([1, 15]);
35513         var _oscCache;
35514         var _oscSelectedImage;
35515
35516
35517         function abortRequest$4(controller) {
35518             controller.abort();
35519         }
35520
35521
35522         function maxPageAtZoom$1(z) {
35523             if (z < 15)   { return 2; }
35524             if (z === 15) { return 5; }
35525             if (z === 16) { return 10; }
35526             if (z === 17) { return 20; }
35527             if (z === 18) { return 40; }
35528             if (z > 18)   { return 80; }
35529         }
35530
35531
35532         function loadTiles$1(which, url, projection) {
35533             var currZoom = Math.floor(geoScaleToZoom(projection.scale()));
35534             var tiles = tiler$4.getTiles(projection);
35535
35536             // abort inflight requests that are no longer needed
35537             var cache = _oscCache[which];
35538             Object.keys(cache.inflight).forEach(function(k) {
35539                 var wanted = tiles.find(function(tile) { return k.indexOf(tile.id + ',') === 0; });
35540                 if (!wanted) {
35541                     abortRequest$4(cache.inflight[k]);
35542                     delete cache.inflight[k];
35543                 }
35544             });
35545
35546             tiles.forEach(function(tile) {
35547                 loadNextTilePage$1(which, currZoom, url, tile);
35548             });
35549         }
35550
35551
35552         function loadNextTilePage$1(which, currZoom, url, tile) {
35553             var cache = _oscCache[which];
35554             var bbox = tile.extent.bbox();
35555             var maxPages = maxPageAtZoom$1(currZoom);
35556             var nextPage = cache.nextPage[tile.id] || 1;
35557             var params = utilQsString({
35558                 ipp: maxResults$1,
35559                 page: nextPage,
35560                 // client_id: clientId,
35561                 bbTopLeft: [bbox.maxY, bbox.minX].join(','),
35562                 bbBottomRight: [bbox.minY, bbox.maxX].join(',')
35563             }, true);
35564
35565             if (nextPage > maxPages) { return; }
35566
35567             var id = tile.id + ',' + String(nextPage);
35568             if (cache.loaded[id] || cache.inflight[id]) { return; }
35569
35570             var controller = new AbortController();
35571             cache.inflight[id] = controller;
35572
35573             var options = {
35574                 method: 'POST',
35575                 signal: controller.signal,
35576                 body: params,
35577                 headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
35578             };
35579
35580             d3_json(url, options)
35581                 .then(function(data) {
35582                     cache.loaded[id] = true;
35583                     delete cache.inflight[id];
35584                     if (!data || !data.currentPageItems || !data.currentPageItems.length) {
35585                         throw new Error('No Data');
35586                     }
35587
35588                     var features = data.currentPageItems.map(function(item) {
35589                         var loc = [+item.lng, +item.lat];
35590                         var d;
35591
35592                         if (which === 'images') {
35593                             d = {
35594                                 loc: loc,
35595                                 key: item.id,
35596                                 ca: +item.heading,
35597                                 captured_at: (item.shot_date || item.date_added),
35598                                 captured_by: item.username,
35599                                 imagePath: item.lth_name,
35600                                 sequence_id: item.sequence_id,
35601                                 sequence_index: +item.sequence_index
35602                             };
35603
35604                             // cache sequence info
35605                             var seq = _oscCache.sequences[d.sequence_id];
35606                             if (!seq) {
35607                                 seq = { rotation: 0, images: [] };
35608                                 _oscCache.sequences[d.sequence_id] = seq;
35609                             }
35610                             seq.images[d.sequence_index] = d;
35611                         }
35612
35613                         return {
35614                             minX: loc[0], minY: loc[1], maxX: loc[0], maxY: loc[1], data: d
35615                         };
35616                     });
35617
35618                     cache.rtree.load(features);
35619
35620                     if (data.currentPageItems.length === maxResults$1) {  // more pages to load
35621                         cache.nextPage[tile.id] = nextPage + 1;
35622                         loadNextTilePage$1(which, currZoom, url, tile);
35623                     } else {
35624                         cache.nextPage[tile.id] = Infinity;     // no more pages to load
35625                     }
35626
35627                     if (which === 'images') {
35628                         dispatch$5.call('loadedImages');
35629                     }
35630                 })
35631                 .catch(function() {
35632                     cache.loaded[id] = true;
35633                     delete cache.inflight[id];
35634                 });
35635         }
35636
35637
35638         // partition viewport into higher zoom tiles
35639         function partitionViewport$1(projection) {
35640             var z = geoScaleToZoom(projection.scale());
35641             var z2 = (Math.ceil(z * 2) / 2) + 2.5;   // round to next 0.5 and add 2.5
35642             var tiler = utilTiler().zoomExtent([z2, z2]);
35643
35644             return tiler.getTiles(projection)
35645                 .map(function(tile) { return tile.extent; });
35646         }
35647
35648
35649         // no more than `limit` results per partition.
35650         function searchLimited$1(limit, projection, rtree) {
35651             limit = limit || 5;
35652
35653             return partitionViewport$1(projection)
35654                 .reduce(function(result, extent) {
35655                     var found = rtree.search(extent.bbox())
35656                         .slice(0, limit)
35657                         .map(function(d) { return d.data; });
35658
35659                     return (found.length ? result.concat(found) : result);
35660                 }, []);
35661         }
35662
35663
35664         var serviceOpenstreetcam = {
35665
35666             init: function() {
35667                 if (!_oscCache) {
35668                     this.reset();
35669                 }
35670
35671                 this.event = utilRebind(this, dispatch$5, 'on');
35672             },
35673
35674             reset: function() {
35675                 if (_oscCache) {
35676                     Object.values(_oscCache.images.inflight).forEach(abortRequest$4);
35677                 }
35678
35679                 _oscCache = {
35680                     images: { inflight: {}, loaded: {}, nextPage: {}, rtree: new RBush() },
35681                     sequences: {}
35682                 };
35683
35684                 _oscSelectedImage = null;
35685             },
35686
35687
35688             images: function(projection) {
35689                 var limit = 5;
35690                 return searchLimited$1(limit, projection, _oscCache.images.rtree);
35691             },
35692
35693
35694             sequences: function(projection) {
35695                 var viewport = projection.clipExtent();
35696                 var min = [viewport[0][0], viewport[1][1]];
35697                 var max = [viewport[1][0], viewport[0][1]];
35698                 var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
35699                 var sequenceKeys = {};
35700
35701                 // all sequences for images in viewport
35702                 _oscCache.images.rtree.search(bbox)
35703                     .forEach(function(d) { sequenceKeys[d.data.sequence_id] = true; });
35704
35705                 // make linestrings from those sequences
35706                 var lineStrings = [];
35707                 Object.keys(sequenceKeys)
35708                     .forEach(function(sequenceKey) {
35709                         var seq = _oscCache.sequences[sequenceKey];
35710                         var images = seq && seq.images;
35711                         if (images) {
35712                             lineStrings.push({
35713                                 type: 'LineString',
35714                                 coordinates: images.map(function (d) { return d.loc; }).filter(Boolean),
35715                                 properties: { key: sequenceKey }
35716                             });
35717                         }
35718                     });
35719                 return lineStrings;
35720             },
35721
35722
35723             loadImages: function(projection) {
35724                 var url = apibase$2 + '/1.0/list/nearby-photos/';
35725                 loadTiles$1('images', url, projection);
35726             },
35727
35728
35729             loadViewer: function(context) {
35730                 var that = this;
35731
35732                 // add osc-wrapper
35733                 var wrap = context.container().select('.photoviewer').selectAll('.osc-wrapper')
35734                     .data([0]);
35735
35736                 var wrapEnter = wrap.enter()
35737                     .append('div')
35738                     .attr('class', 'photo-wrapper osc-wrapper')
35739                     .classed('hide', true)
35740                     .call(imgZoom.on('zoom', zoomPan))
35741                     .on('dblclick.zoom', null);
35742
35743                 wrapEnter
35744                     .append('div')
35745                     .attr('class', 'photo-attribution fillD');
35746
35747                 var controlsEnter = wrapEnter
35748                     .append('div')
35749                     .attr('class', 'photo-controls-wrap')
35750                     .append('div')
35751                     .attr('class', 'photo-controls');
35752
35753                 controlsEnter
35754                     .append('button')
35755                     .on('click.back', step(-1))
35756                     .text('◄');
35757
35758                 controlsEnter
35759                     .append('button')
35760                     .on('click.rotate-ccw', rotate(-90))
35761                     .text('⤿');
35762
35763                 controlsEnter
35764                     .append('button')
35765                     .on('click.rotate-cw', rotate(90))
35766                     .text('⤾');
35767
35768                 controlsEnter
35769                     .append('button')
35770                     .on('click.forward', step(1))
35771                     .text('►');
35772
35773                 wrapEnter
35774                     .append('div')
35775                     .attr('class', 'osc-image-wrap');
35776
35777
35778                 // Register viewer resize handler
35779                 context.ui().photoviewer.on('resize.openstreetcam', function(dimensions) {
35780                     imgZoom = d3_zoom()
35781                         .extent([[0, 0], dimensions])
35782                         .translateExtent([[0, 0], dimensions])
35783                         .scaleExtent([1, 15])
35784                         .on('zoom', zoomPan);
35785                 });
35786
35787
35788                 function zoomPan() {
35789                     var t = event.transform;
35790                     context.container().select('.photoviewer .osc-image-wrap')
35791                         .call(utilSetTransform, t.x, t.y, t.k);
35792                 }
35793
35794
35795                 function rotate(deg) {
35796                     return function() {
35797                         if (!_oscSelectedImage) { return; }
35798                         var sequenceKey = _oscSelectedImage.sequence_id;
35799                         var sequence = _oscCache.sequences[sequenceKey];
35800                         if (!sequence) { return; }
35801
35802                         var r = sequence.rotation || 0;
35803                         r += deg;
35804
35805                         if (r > 180) { r -= 360; }
35806                         if (r < -180) { r += 360; }
35807                         sequence.rotation = r;
35808
35809                         var wrap = context.container().select('.photoviewer .osc-wrapper');
35810
35811                         wrap
35812                             .transition()
35813                             .duration(100)
35814                             .call(imgZoom.transform, identity$2);
35815
35816                         wrap.selectAll('.osc-image')
35817                             .transition()
35818                             .duration(100)
35819                             .style('transform', 'rotate(' + r + 'deg)');
35820                     };
35821                 }
35822
35823                 function step(stepBy) {
35824                     return function() {
35825                         if (!_oscSelectedImage) { return; }
35826                         var sequenceKey = _oscSelectedImage.sequence_id;
35827                         var sequence = _oscCache.sequences[sequenceKey];
35828                         if (!sequence) { return; }
35829
35830                         var nextIndex = _oscSelectedImage.sequence_index + stepBy;
35831                         var nextImage = sequence.images[nextIndex];
35832                         if (!nextImage) { return; }
35833
35834                         context.map().centerEase(nextImage.loc);
35835
35836                         that
35837                             .selectImage(context, nextImage)
35838                             .updateViewer(context, nextImage);
35839                     };
35840                 }
35841             },
35842
35843
35844             showViewer: function(context) {
35845                 var viewer = context.container().select('.photoviewer')
35846                     .classed('hide', false);
35847
35848                 var isHidden = viewer.selectAll('.photo-wrapper.osc-wrapper.hide').size();
35849
35850                 if (isHidden) {
35851                     viewer
35852                         .selectAll('.photo-wrapper:not(.osc-wrapper)')
35853                         .classed('hide', true);
35854
35855                     viewer
35856                         .selectAll('.photo-wrapper.osc-wrapper')
35857                         .classed('hide', false);
35858                 }
35859
35860                 return this;
35861             },
35862
35863
35864             hideViewer: function(context) {
35865                 _oscSelectedImage = null;
35866
35867                 var viewer = context.container().select('.photoviewer');
35868                 if (!viewer.empty()) { viewer.datum(null); }
35869
35870                 viewer
35871                     .classed('hide', true)
35872                     .selectAll('.photo-wrapper')
35873                     .classed('hide', true);
35874
35875                 context.container().selectAll('.viewfield-group, .sequence, .icon-sign')
35876                     .classed('currentView', false);
35877
35878                 return this.setStyles(context, null, true);
35879             },
35880
35881
35882             updateViewer: function(context, d) {
35883                 var wrap = context.container().select('.photoviewer .osc-wrapper');
35884                 var imageWrap = wrap.selectAll('.osc-image-wrap');
35885                 var attribution = wrap.selectAll('.photo-attribution').html('');
35886
35887                 wrap
35888                     .transition()
35889                     .duration(100)
35890                     .call(imgZoom.transform, identity$2);
35891
35892                 imageWrap
35893                     .selectAll('.osc-image')
35894                     .remove();
35895
35896                 if (d) {
35897                     var sequence = _oscCache.sequences[d.sequence_id];
35898                     var r = (sequence && sequence.rotation) || 0;
35899
35900                     imageWrap
35901                         .append('img')
35902                         .attr('class', 'osc-image')
35903                         .attr('src', apibase$2 + '/' + d.imagePath)
35904                         .style('transform', 'rotate(' + r + 'deg)');
35905
35906                     if (d.captured_by) {
35907                         attribution
35908                             .append('a')
35909                             .attr('class', 'captured_by')
35910                             .attr('target', '_blank')
35911                             .attr('href', 'https://openstreetcam.org/user/' + encodeURIComponent(d.captured_by))
35912                             .text('@' + d.captured_by);
35913
35914                         attribution
35915                             .append('span')
35916                             .text('|');
35917                     }
35918
35919                     if (d.captured_at) {
35920                         attribution
35921                             .append('span')
35922                             .attr('class', 'captured_at')
35923                             .text(localeDateString(d.captured_at));
35924
35925                         attribution
35926                             .append('span')
35927                             .text('|');
35928                     }
35929
35930                     attribution
35931                         .append('a')
35932                         .attr('class', 'image-link')
35933                         .attr('target', '_blank')
35934                         .attr('href', 'https://openstreetcam.org/details/' + d.sequence_id + '/' + d.sequence_index)
35935                         .text('openstreetcam.org');
35936                 }
35937
35938                 return this;
35939
35940
35941                 function localeDateString(s) {
35942                     if (!s) { return null; }
35943                     var options = { day: 'numeric', month: 'short', year: 'numeric' };
35944                     var d = new Date(s);
35945                     if (isNaN(d.getTime())) { return null; }
35946                     return d.toLocaleDateString(_mainLocalizer.localeCode(), options);
35947                 }
35948             },
35949
35950
35951             selectImage: function(context, d) {
35952                 _oscSelectedImage = d;
35953                 var viewer = context.container().select('.photoviewer');
35954                 if (!viewer.empty()) { viewer.datum(d); }
35955
35956                 this.setStyles(context, null, true);
35957
35958                 context.container().selectAll('.icon-sign')
35959                     .classed('currentView', false);
35960
35961                 return this;
35962             },
35963
35964
35965             getSelectedImage: function() {
35966                 return _oscSelectedImage;
35967             },
35968
35969
35970             getSequenceKeyForImage: function(d) {
35971                 return d && d.sequence_id;
35972             },
35973
35974
35975             // Updates the currently highlighted sequence and selected bubble.
35976             // Reset is only necessary when interacting with the viewport because
35977             // this implicitly changes the currently selected bubble/sequence
35978             setStyles: function(context, hovered, reset) {
35979                 if (reset) {  // reset all layers
35980                     context.container().selectAll('.viewfield-group')
35981                         .classed('highlighted', false)
35982                         .classed('hovered', false)
35983                         .classed('currentView', false);
35984
35985                     context.container().selectAll('.sequence')
35986                         .classed('highlighted', false)
35987                         .classed('currentView', false);
35988                 }
35989
35990                 var hoveredImageKey = hovered && hovered.key;
35991                 var hoveredSequenceKey = this.getSequenceKeyForImage(hovered);
35992                 var hoveredSequence = hoveredSequenceKey && _oscCache.sequences[hoveredSequenceKey];
35993                 var hoveredImageKeys = (hoveredSequence && hoveredSequence.images.map(function (d) { return d.key; })) || [];
35994
35995                 var viewer = context.container().select('.photoviewer');
35996                 var selected = viewer.empty() ? undefined : viewer.datum();
35997                 var selectedImageKey = selected && selected.key;
35998                 var selectedSequenceKey = this.getSequenceKeyForImage(selected);
35999                 var selectedSequence = selectedSequenceKey && _oscCache.sequences[selectedSequenceKey];
36000                 var selectedImageKeys = (selectedSequence && selectedSequence.images.map(function (d) { return d.key; })) || [];
36001
36002                 // highlight sibling viewfields on either the selected or the hovered sequences
36003                 var highlightedImageKeys = utilArrayUnion(hoveredImageKeys, selectedImageKeys);
36004
36005                 context.container().selectAll('.layer-openstreetcam .viewfield-group')
36006                     .classed('highlighted', function(d) { return highlightedImageKeys.indexOf(d.key) !== -1; })
36007                     .classed('hovered', function(d) { return d.key === hoveredImageKey; })
36008                     .classed('currentView', function(d) { return d.key === selectedImageKey; });
36009
36010                 context.container().selectAll('.layer-openstreetcam .sequence')
36011                     .classed('highlighted', function(d) { return d.properties.key === hoveredSequenceKey; })
36012                     .classed('currentView', function(d) { return d.properties.key === selectedSequenceKey; });
36013
36014                 // update viewfields if needed
36015                 context.container().selectAll('.viewfield-group .viewfield')
36016                     .attr('d', viewfieldPath);
36017
36018                 function viewfieldPath() {
36019                     var d = this.parentNode.__data__;
36020                     if (d.pano && d.key !== selectedImageKey) {
36021                         return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0';
36022                     } else {
36023                         return 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z';
36024                     }
36025                 }
36026
36027                 return this;
36028             },
36029
36030
36031             cache: function() {
36032                 return _oscCache;
36033             }
36034
36035         };
36036
36037         /**
36038          * Checks if `value` is the
36039          * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)
36040          * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
36041          *
36042          * @static
36043          * @memberOf _
36044          * @since 0.1.0
36045          * @category Lang
36046          * @param {*} value The value to check.
36047          * @returns {boolean} Returns `true` if `value` is an object, else `false`.
36048          * @example
36049          *
36050          * _.isObject({});
36051          * // => true
36052          *
36053          * _.isObject([1, 2, 3]);
36054          * // => true
36055          *
36056          * _.isObject(_.noop);
36057          * // => true
36058          *
36059          * _.isObject(null);
36060          * // => false
36061          */
36062         function isObject$1(value) {
36063           var type = typeof value;
36064           return value != null && (type == 'object' || type == 'function');
36065         }
36066
36067         /** Detect free variable `global` from Node.js. */
36068         var freeGlobal = typeof global == 'object' && global && global.Object === Object && global;
36069
36070         /** Detect free variable `self`. */
36071         var freeSelf = typeof self == 'object' && self && self.Object === Object && self;
36072
36073         /** Used as a reference to the global object. */
36074         var root$2 = freeGlobal || freeSelf || Function('return this')();
36075
36076         /**
36077          * Gets the timestamp of the number of milliseconds that have elapsed since
36078          * the Unix epoch (1 January 1970 00:00:00 UTC).
36079          *
36080          * @static
36081          * @memberOf _
36082          * @since 2.4.0
36083          * @category Date
36084          * @returns {number} Returns the timestamp.
36085          * @example
36086          *
36087          * _.defer(function(stamp) {
36088          *   console.log(_.now() - stamp);
36089          * }, _.now());
36090          * // => Logs the number of milliseconds it took for the deferred invocation.
36091          */
36092         var now$1 = function() {
36093           return root$2.Date.now();
36094         };
36095
36096         /** Built-in value references. */
36097         var Symbol$1 = root$2.Symbol;
36098
36099         /** Used for built-in method references. */
36100         var objectProto = Object.prototype;
36101
36102         /** Used to check objects for own properties. */
36103         var hasOwnProperty$2 = objectProto.hasOwnProperty;
36104
36105         /**
36106          * Used to resolve the
36107          * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
36108          * of values.
36109          */
36110         var nativeObjectToString = objectProto.toString;
36111
36112         /** Built-in value references. */
36113         var symToStringTag = Symbol$1 ? Symbol$1.toStringTag : undefined;
36114
36115         /**
36116          * A specialized version of `baseGetTag` which ignores `Symbol.toStringTag` values.
36117          *
36118          * @private
36119          * @param {*} value The value to query.
36120          * @returns {string} Returns the raw `toStringTag`.
36121          */
36122         function getRawTag(value) {
36123           var isOwn = hasOwnProperty$2.call(value, symToStringTag),
36124               tag = value[symToStringTag];
36125
36126           try {
36127             value[symToStringTag] = undefined;
36128             var unmasked = true;
36129           } catch (e) {}
36130
36131           var result = nativeObjectToString.call(value);
36132           if (unmasked) {
36133             if (isOwn) {
36134               value[symToStringTag] = tag;
36135             } else {
36136               delete value[symToStringTag];
36137             }
36138           }
36139           return result;
36140         }
36141
36142         /** Used for built-in method references. */
36143         var objectProto$1 = Object.prototype;
36144
36145         /**
36146          * Used to resolve the
36147          * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
36148          * of values.
36149          */
36150         var nativeObjectToString$1 = objectProto$1.toString;
36151
36152         /**
36153          * Converts `value` to a string using `Object.prototype.toString`.
36154          *
36155          * @private
36156          * @param {*} value The value to convert.
36157          * @returns {string} Returns the converted string.
36158          */
36159         function objectToString$2(value) {
36160           return nativeObjectToString$1.call(value);
36161         }
36162
36163         /** `Object#toString` result references. */
36164         var nullTag = '[object Null]',
36165             undefinedTag = '[object Undefined]';
36166
36167         /** Built-in value references. */
36168         var symToStringTag$1 = Symbol$1 ? Symbol$1.toStringTag : undefined;
36169
36170         /**
36171          * The base implementation of `getTag` without fallbacks for buggy environments.
36172          *
36173          * @private
36174          * @param {*} value The value to query.
36175          * @returns {string} Returns the `toStringTag`.
36176          */
36177         function baseGetTag(value) {
36178           if (value == null) {
36179             return value === undefined ? undefinedTag : nullTag;
36180           }
36181           return (symToStringTag$1 && symToStringTag$1 in Object(value))
36182             ? getRawTag(value)
36183             : objectToString$2(value);
36184         }
36185
36186         /**
36187          * Checks if `value` is object-like. A value is object-like if it's not `null`
36188          * and has a `typeof` result of "object".
36189          *
36190          * @static
36191          * @memberOf _
36192          * @since 4.0.0
36193          * @category Lang
36194          * @param {*} value The value to check.
36195          * @returns {boolean} Returns `true` if `value` is object-like, else `false`.
36196          * @example
36197          *
36198          * _.isObjectLike({});
36199          * // => true
36200          *
36201          * _.isObjectLike([1, 2, 3]);
36202          * // => true
36203          *
36204          * _.isObjectLike(_.noop);
36205          * // => false
36206          *
36207          * _.isObjectLike(null);
36208          * // => false
36209          */
36210         function isObjectLike(value) {
36211           return value != null && typeof value == 'object';
36212         }
36213
36214         /** `Object#toString` result references. */
36215         var symbolTag = '[object Symbol]';
36216
36217         /**
36218          * Checks if `value` is classified as a `Symbol` primitive or object.
36219          *
36220          * @static
36221          * @memberOf _
36222          * @since 4.0.0
36223          * @category Lang
36224          * @param {*} value The value to check.
36225          * @returns {boolean} Returns `true` if `value` is a symbol, else `false`.
36226          * @example
36227          *
36228          * _.isSymbol(Symbol.iterator);
36229          * // => true
36230          *
36231          * _.isSymbol('abc');
36232          * // => false
36233          */
36234         function isSymbol$4(value) {
36235           return typeof value == 'symbol' ||
36236             (isObjectLike(value) && baseGetTag(value) == symbolTag);
36237         }
36238
36239         /** Used as references for various `Number` constants. */
36240         var NAN = 0 / 0;
36241
36242         /** Used to match leading and trailing whitespace. */
36243         var reTrim = /^\s+|\s+$/g;
36244
36245         /** Used to detect bad signed hexadecimal string values. */
36246         var reIsBadHex = /^[-+]0x[0-9a-f]+$/i;
36247
36248         /** Used to detect binary string values. */
36249         var reIsBinary = /^0b[01]+$/i;
36250
36251         /** Used to detect octal string values. */
36252         var reIsOctal = /^0o[0-7]+$/i;
36253
36254         /** Built-in method references without a dependency on `root`. */
36255         var freeParseInt = parseInt;
36256
36257         /**
36258          * Converts `value` to a number.
36259          *
36260          * @static
36261          * @memberOf _
36262          * @since 4.0.0
36263          * @category Lang
36264          * @param {*} value The value to process.
36265          * @returns {number} Returns the number.
36266          * @example
36267          *
36268          * _.toNumber(3.2);
36269          * // => 3.2
36270          *
36271          * _.toNumber(Number.MIN_VALUE);
36272          * // => 5e-324
36273          *
36274          * _.toNumber(Infinity);
36275          * // => Infinity
36276          *
36277          * _.toNumber('3.2');
36278          * // => 3.2
36279          */
36280         function toNumber(value) {
36281           if (typeof value == 'number') {
36282             return value;
36283           }
36284           if (isSymbol$4(value)) {
36285             return NAN;
36286           }
36287           if (isObject$1(value)) {
36288             var other = typeof value.valueOf == 'function' ? value.valueOf() : value;
36289             value = isObject$1(other) ? (other + '') : other;
36290           }
36291           if (typeof value != 'string') {
36292             return value === 0 ? value : +value;
36293           }
36294           value = value.replace(reTrim, '');
36295           var isBinary = reIsBinary.test(value);
36296           return (isBinary || reIsOctal.test(value))
36297             ? freeParseInt(value.slice(2), isBinary ? 2 : 8)
36298             : (reIsBadHex.test(value) ? NAN : +value);
36299         }
36300
36301         /** Error message constants. */
36302         var FUNC_ERROR_TEXT = 'Expected a function';
36303
36304         /* Built-in method references for those with the same name as other `lodash` methods. */
36305         var nativeMax = Math.max,
36306             nativeMin = Math.min;
36307
36308         /**
36309          * Creates a debounced function that delays invoking `func` until after `wait`
36310          * milliseconds have elapsed since the last time the debounced function was
36311          * invoked. The debounced function comes with a `cancel` method to cancel
36312          * delayed `func` invocations and a `flush` method to immediately invoke them.
36313          * Provide `options` to indicate whether `func` should be invoked on the
36314          * leading and/or trailing edge of the `wait` timeout. The `func` is invoked
36315          * with the last arguments provided to the debounced function. Subsequent
36316          * calls to the debounced function return the result of the last `func`
36317          * invocation.
36318          *
36319          * **Note:** If `leading` and `trailing` options are `true`, `func` is
36320          * invoked on the trailing edge of the timeout only if the debounced function
36321          * is invoked more than once during the `wait` timeout.
36322          *
36323          * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
36324          * until to the next tick, similar to `setTimeout` with a timeout of `0`.
36325          *
36326          * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
36327          * for details over the differences between `_.debounce` and `_.throttle`.
36328          *
36329          * @static
36330          * @memberOf _
36331          * @since 0.1.0
36332          * @category Function
36333          * @param {Function} func The function to debounce.
36334          * @param {number} [wait=0] The number of milliseconds to delay.
36335          * @param {Object} [options={}] The options object.
36336          * @param {boolean} [options.leading=false]
36337          *  Specify invoking on the leading edge of the timeout.
36338          * @param {number} [options.maxWait]
36339          *  The maximum time `func` is allowed to be delayed before it's invoked.
36340          * @param {boolean} [options.trailing=true]
36341          *  Specify invoking on the trailing edge of the timeout.
36342          * @returns {Function} Returns the new debounced function.
36343          * @example
36344          *
36345          * // Avoid costly calculations while the window size is in flux.
36346          * jQuery(window).on('resize', _.debounce(calculateLayout, 150));
36347          *
36348          * // Invoke `sendMail` when clicked, debouncing subsequent calls.
36349          * jQuery(element).on('click', _.debounce(sendMail, 300, {
36350          *   'leading': true,
36351          *   'trailing': false
36352          * }));
36353          *
36354          * // Ensure `batchLog` is invoked once after 1 second of debounced calls.
36355          * var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 });
36356          * var source = new EventSource('/stream');
36357          * jQuery(source).on('message', debounced);
36358          *
36359          * // Cancel the trailing debounced invocation.
36360          * jQuery(window).on('popstate', debounced.cancel);
36361          */
36362         function debounce(func, wait, options) {
36363           var lastArgs,
36364               lastThis,
36365               maxWait,
36366               result,
36367               timerId,
36368               lastCallTime,
36369               lastInvokeTime = 0,
36370               leading = false,
36371               maxing = false,
36372               trailing = true;
36373
36374           if (typeof func != 'function') {
36375             throw new TypeError(FUNC_ERROR_TEXT);
36376           }
36377           wait = toNumber(wait) || 0;
36378           if (isObject$1(options)) {
36379             leading = !!options.leading;
36380             maxing = 'maxWait' in options;
36381             maxWait = maxing ? nativeMax(toNumber(options.maxWait) || 0, wait) : maxWait;
36382             trailing = 'trailing' in options ? !!options.trailing : trailing;
36383           }
36384
36385           function invokeFunc(time) {
36386             var args = lastArgs,
36387                 thisArg = lastThis;
36388
36389             lastArgs = lastThis = undefined;
36390             lastInvokeTime = time;
36391             result = func.apply(thisArg, args);
36392             return result;
36393           }
36394
36395           function leadingEdge(time) {
36396             // Reset any `maxWait` timer.
36397             lastInvokeTime = time;
36398             // Start the timer for the trailing edge.
36399             timerId = setTimeout(timerExpired, wait);
36400             // Invoke the leading edge.
36401             return leading ? invokeFunc(time) : result;
36402           }
36403
36404           function remainingWait(time) {
36405             var timeSinceLastCall = time - lastCallTime,
36406                 timeSinceLastInvoke = time - lastInvokeTime,
36407                 timeWaiting = wait - timeSinceLastCall;
36408
36409             return maxing
36410               ? nativeMin(timeWaiting, maxWait - timeSinceLastInvoke)
36411               : timeWaiting;
36412           }
36413
36414           function shouldInvoke(time) {
36415             var timeSinceLastCall = time - lastCallTime,
36416                 timeSinceLastInvoke = time - lastInvokeTime;
36417
36418             // Either this is the first call, activity has stopped and we're at the
36419             // trailing edge, the system time has gone backwards and we're treating
36420             // it as the trailing edge, or we've hit the `maxWait` limit.
36421             return (lastCallTime === undefined || (timeSinceLastCall >= wait) ||
36422               (timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait));
36423           }
36424
36425           function timerExpired() {
36426             var time = now$1();
36427             if (shouldInvoke(time)) {
36428               return trailingEdge(time);
36429             }
36430             // Restart the timer.
36431             timerId = setTimeout(timerExpired, remainingWait(time));
36432           }
36433
36434           function trailingEdge(time) {
36435             timerId = undefined;
36436
36437             // Only invoke if we have `lastArgs` which means `func` has been
36438             // debounced at least once.
36439             if (trailing && lastArgs) {
36440               return invokeFunc(time);
36441             }
36442             lastArgs = lastThis = undefined;
36443             return result;
36444           }
36445
36446           function cancel() {
36447             if (timerId !== undefined) {
36448               clearTimeout(timerId);
36449             }
36450             lastInvokeTime = 0;
36451             lastArgs = lastCallTime = lastThis = timerId = undefined;
36452           }
36453
36454           function flush() {
36455             return timerId === undefined ? result : trailingEdge(now$1());
36456           }
36457
36458           function debounced() {
36459             var time = now$1(),
36460                 isInvoking = shouldInvoke(time);
36461
36462             lastArgs = arguments;
36463             lastThis = this;
36464             lastCallTime = time;
36465
36466             if (isInvoking) {
36467               if (timerId === undefined) {
36468                 return leadingEdge(lastCallTime);
36469               }
36470               if (maxing) {
36471                 // Handle invocations in a tight loop.
36472                 clearTimeout(timerId);
36473                 timerId = setTimeout(timerExpired, wait);
36474                 return invokeFunc(lastCallTime);
36475               }
36476             }
36477             if (timerId === undefined) {
36478               timerId = setTimeout(timerExpired, wait);
36479             }
36480             return result;
36481           }
36482           debounced.cancel = cancel;
36483           debounced.flush = flush;
36484           return debounced;
36485         }
36486
36487         /** Error message constants. */
36488         var FUNC_ERROR_TEXT$1 = 'Expected a function';
36489
36490         /**
36491          * Creates a throttled function that only invokes `func` at most once per
36492          * every `wait` milliseconds. The throttled function comes with a `cancel`
36493          * method to cancel delayed `func` invocations and a `flush` method to
36494          * immediately invoke them. Provide `options` to indicate whether `func`
36495          * should be invoked on the leading and/or trailing edge of the `wait`
36496          * timeout. The `func` is invoked with the last arguments provided to the
36497          * throttled function. Subsequent calls to the throttled function return the
36498          * result of the last `func` invocation.
36499          *
36500          * **Note:** If `leading` and `trailing` options are `true`, `func` is
36501          * invoked on the trailing edge of the timeout only if the throttled function
36502          * is invoked more than once during the `wait` timeout.
36503          *
36504          * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
36505          * until to the next tick, similar to `setTimeout` with a timeout of `0`.
36506          *
36507          * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
36508          * for details over the differences between `_.throttle` and `_.debounce`.
36509          *
36510          * @static
36511          * @memberOf _
36512          * @since 0.1.0
36513          * @category Function
36514          * @param {Function} func The function to throttle.
36515          * @param {number} [wait=0] The number of milliseconds to throttle invocations to.
36516          * @param {Object} [options={}] The options object.
36517          * @param {boolean} [options.leading=true]
36518          *  Specify invoking on the leading edge of the timeout.
36519          * @param {boolean} [options.trailing=true]
36520          *  Specify invoking on the trailing edge of the timeout.
36521          * @returns {Function} Returns the new throttled function.
36522          * @example
36523          *
36524          * // Avoid excessively updating the position while scrolling.
36525          * jQuery(window).on('scroll', _.throttle(updatePosition, 100));
36526          *
36527          * // Invoke `renewToken` when the click event is fired, but not more than once every 5 minutes.
36528          * var throttled = _.throttle(renewToken, 300000, { 'trailing': false });
36529          * jQuery(element).on('click', throttled);
36530          *
36531          * // Cancel the trailing throttled invocation.
36532          * jQuery(window).on('popstate', throttled.cancel);
36533          */
36534         function throttle(func, wait, options) {
36535           var leading = true,
36536               trailing = true;
36537
36538           if (typeof func != 'function') {
36539             throw new TypeError(FUNC_ERROR_TEXT$1);
36540           }
36541           if (isObject$1(options)) {
36542             leading = 'leading' in options ? !!options.leading : leading;
36543             trailing = 'trailing' in options ? !!options.trailing : trailing;
36544           }
36545           return debounce(func, wait, {
36546             'leading': leading,
36547             'maxWait': wait,
36548             'trailing': trailing
36549           });
36550         }
36551
36552         var hashes = createCommonjsModule(function (module, exports) {
36553         /**
36554          * jshashes - https://github.com/h2non/jshashes
36555          * Released under the "New BSD" license
36556          *
36557          * Algorithms specification:
36558          *
36559          * MD5 - http://www.ietf.org/rfc/rfc1321.txt
36560          * RIPEMD-160 - http://homes.esat.kuleuven.be/~bosselae/ripemd160.html
36561          * SHA1   - http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf
36562          * SHA256 - http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf
36563          * SHA512 - http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf
36564          * HMAC - http://www.ietf.org/rfc/rfc2104.txt
36565          */
36566         (function() {
36567           var Hashes;
36568
36569           function utf8Encode(str) {
36570             var x, y, output = '',
36571               i = -1,
36572               l;
36573
36574             if (str && str.length) {
36575               l = str.length;
36576               while ((i += 1) < l) {
36577                 /* Decode utf-16 surrogate pairs */
36578                 x = str.charCodeAt(i);
36579                 y = i + 1 < l ? str.charCodeAt(i + 1) : 0;
36580                 if (0xD800 <= x && x <= 0xDBFF && 0xDC00 <= y && y <= 0xDFFF) {
36581                   x = 0x10000 + ((x & 0x03FF) << 10) + (y & 0x03FF);
36582                   i += 1;
36583                 }
36584                 /* Encode output as utf-8 */
36585                 if (x <= 0x7F) {
36586                   output += String.fromCharCode(x);
36587                 } else if (x <= 0x7FF) {
36588                   output += String.fromCharCode(0xC0 | ((x >>> 6) & 0x1F),
36589                     0x80 | (x & 0x3F));
36590                 } else if (x <= 0xFFFF) {
36591                   output += String.fromCharCode(0xE0 | ((x >>> 12) & 0x0F),
36592                     0x80 | ((x >>> 6) & 0x3F),
36593                     0x80 | (x & 0x3F));
36594                 } else if (x <= 0x1FFFFF) {
36595                   output += String.fromCharCode(0xF0 | ((x >>> 18) & 0x07),
36596                     0x80 | ((x >>> 12) & 0x3F),
36597                     0x80 | ((x >>> 6) & 0x3F),
36598                     0x80 | (x & 0x3F));
36599                 }
36600               }
36601             }
36602             return output;
36603           }
36604
36605           function utf8Decode(str) {
36606             var i, ac, c1, c2, c3, arr = [],
36607               l;
36608             i = ac = c1 = c2 = c3 = 0;
36609
36610             if (str && str.length) {
36611               l = str.length;
36612               str += '';
36613
36614               while (i < l) {
36615                 c1 = str.charCodeAt(i);
36616                 ac += 1;
36617                 if (c1 < 128) {
36618                   arr[ac] = String.fromCharCode(c1);
36619                   i += 1;
36620                 } else if (c1 > 191 && c1 < 224) {
36621                   c2 = str.charCodeAt(i + 1);
36622                   arr[ac] = String.fromCharCode(((c1 & 31) << 6) | (c2 & 63));
36623                   i += 2;
36624                 } else {
36625                   c2 = str.charCodeAt(i + 1);
36626                   c3 = str.charCodeAt(i + 2);
36627                   arr[ac] = String.fromCharCode(((c1 & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
36628                   i += 3;
36629                 }
36630               }
36631             }
36632             return arr.join('');
36633           }
36634
36635           /**
36636            * Add integers, wrapping at 2^32. This uses 16-bit operations internally
36637            * to work around bugs in some JS interpreters.
36638            */
36639
36640           function safe_add(x, y) {
36641             var lsw = (x & 0xFFFF) + (y & 0xFFFF),
36642               msw = (x >> 16) + (y >> 16) + (lsw >> 16);
36643             return (msw << 16) | (lsw & 0xFFFF);
36644           }
36645
36646           /**
36647            * Bitwise rotate a 32-bit number to the left.
36648            */
36649
36650           function bit_rol(num, cnt) {
36651             return (num << cnt) | (num >>> (32 - cnt));
36652           }
36653
36654           /**
36655            * Convert a raw string to a hex string
36656            */
36657
36658           function rstr2hex(input, hexcase) {
36659             var hex_tab = hexcase ? '0123456789ABCDEF' : '0123456789abcdef',
36660               output = '',
36661               x, i = 0,
36662               l = input.length;
36663             for (; i < l; i += 1) {
36664               x = input.charCodeAt(i);
36665               output += hex_tab.charAt((x >>> 4) & 0x0F) + hex_tab.charAt(x & 0x0F);
36666             }
36667             return output;
36668           }
36669
36670           /**
36671            * Convert an array of big-endian words to a string
36672            */
36673
36674           function binb2rstr(input) {
36675             var i, l = input.length * 32,
36676               output = '';
36677             for (i = 0; i < l; i += 8) {
36678               output += String.fromCharCode((input[i >> 5] >>> (24 - i % 32)) & 0xFF);
36679             }
36680             return output;
36681           }
36682
36683           /**
36684            * Convert an array of little-endian words to a string
36685            */
36686
36687           function binl2rstr(input) {
36688             var i, l = input.length * 32,
36689               output = '';
36690             for (i = 0; i < l; i += 8) {
36691               output += String.fromCharCode((input[i >> 5] >>> (i % 32)) & 0xFF);
36692             }
36693             return output;
36694           }
36695
36696           /**
36697            * Convert a raw string to an array of little-endian words
36698            * Characters >255 have their high-byte silently ignored.
36699            */
36700
36701           function rstr2binl(input) {
36702             var i, l = input.length * 8,
36703               output = Array(input.length >> 2),
36704               lo = output.length;
36705             for (i = 0; i < lo; i += 1) {
36706               output[i] = 0;
36707             }
36708             for (i = 0; i < l; i += 8) {
36709               output[i >> 5] |= (input.charCodeAt(i / 8) & 0xFF) << (i % 32);
36710             }
36711             return output;
36712           }
36713
36714           /**
36715            * Convert a raw string to an array of big-endian words
36716            * Characters >255 have their high-byte silently ignored.
36717            */
36718
36719           function rstr2binb(input) {
36720             var i, l = input.length * 8,
36721               output = Array(input.length >> 2),
36722               lo = output.length;
36723             for (i = 0; i < lo; i += 1) {
36724               output[i] = 0;
36725             }
36726             for (i = 0; i < l; i += 8) {
36727               output[i >> 5] |= (input.charCodeAt(i / 8) & 0xFF) << (24 - i % 32);
36728             }
36729             return output;
36730           }
36731
36732           /**
36733            * Convert a raw string to an arbitrary string encoding
36734            */
36735
36736           function rstr2any(input, encoding) {
36737             var divisor = encoding.length,
36738               remainders = Array(),
36739               i, q, x, ld, quotient, dividend, output, full_length;
36740
36741             /* Convert to an array of 16-bit big-endian values, forming the dividend */
36742             dividend = Array(Math.ceil(input.length / 2));
36743             ld = dividend.length;
36744             for (i = 0; i < ld; i += 1) {
36745               dividend[i] = (input.charCodeAt(i * 2) << 8) | input.charCodeAt(i * 2 + 1);
36746             }
36747
36748             /**
36749              * Repeatedly perform a long division. The binary array forms the dividend,
36750              * the length of the encoding is the divisor. Once computed, the quotient
36751              * forms the dividend for the next step. We stop when the dividend is zerHashes.
36752              * All remainders are stored for later use.
36753              */
36754             while (dividend.length > 0) {
36755               quotient = Array();
36756               x = 0;
36757               for (i = 0; i < dividend.length; i += 1) {
36758                 x = (x << 16) + dividend[i];
36759                 q = Math.floor(x / divisor);
36760                 x -= q * divisor;
36761                 if (quotient.length > 0 || q > 0) {
36762                   quotient[quotient.length] = q;
36763                 }
36764               }
36765               remainders[remainders.length] = x;
36766               dividend = quotient;
36767             }
36768
36769             /* Convert the remainders to the output string */
36770             output = '';
36771             for (i = remainders.length - 1; i >= 0; i--) {
36772               output += encoding.charAt(remainders[i]);
36773             }
36774
36775             /* Append leading zero equivalents */
36776             full_length = Math.ceil(input.length * 8 / (Math.log(encoding.length) / Math.log(2)));
36777             for (i = output.length; i < full_length; i += 1) {
36778               output = encoding[0] + output;
36779             }
36780             return output;
36781           }
36782
36783           /**
36784            * Convert a raw string to a base-64 string
36785            */
36786
36787           function rstr2b64(input, b64pad) {
36788             var tab = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
36789               output = '',
36790               len = input.length,
36791               i, j, triplet;
36792             b64pad = b64pad || '=';
36793             for (i = 0; i < len; i += 3) {
36794               triplet = (input.charCodeAt(i) << 16) | (i + 1 < len ? input.charCodeAt(i + 1) << 8 : 0) | (i + 2 < len ? input.charCodeAt(i + 2) : 0);
36795               for (j = 0; j < 4; j += 1) {
36796                 if (i * 8 + j * 6 > input.length * 8) {
36797                   output += b64pad;
36798                 } else {
36799                   output += tab.charAt((triplet >>> 6 * (3 - j)) & 0x3F);
36800                 }
36801               }
36802             }
36803             return output;
36804           }
36805
36806           Hashes = {
36807             /**
36808              * @property {String} version
36809              * @readonly
36810              */
36811             VERSION: '1.0.6',
36812             /**
36813              * @member Hashes
36814              * @class Base64
36815              * @constructor
36816              */
36817             Base64: function() {
36818               // private properties
36819               var tab = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
36820                 pad = '=', // default pad according with the RFC standard
36821                 utf8 = true; // by default enable UTF-8 support encoding
36822
36823               // public method for encoding
36824               this.encode = function(input) {
36825                 var i, j, triplet,
36826                   output = '',
36827                   len = input.length;
36828
36829                 pad = pad || '=';
36830                 input = (utf8) ? utf8Encode(input) : input;
36831
36832                 for (i = 0; i < len; i += 3) {
36833                   triplet = (input.charCodeAt(i) << 16) | (i + 1 < len ? input.charCodeAt(i + 1) << 8 : 0) | (i + 2 < len ? input.charCodeAt(i + 2) : 0);
36834                   for (j = 0; j < 4; j += 1) {
36835                     if (i * 8 + j * 6 > len * 8) {
36836                       output += pad;
36837                     } else {
36838                       output += tab.charAt((triplet >>> 6 * (3 - j)) & 0x3F);
36839                     }
36840                   }
36841                 }
36842                 return output;
36843               };
36844
36845               // public method for decoding
36846               this.decode = function(input) {
36847                 // var b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
36848                 var i, o1, o2, o3, h1, h2, h3, h4, bits, ac,
36849                   dec = '',
36850                   arr = [];
36851                 if (!input) {
36852                   return input;
36853                 }
36854
36855                 i = ac = 0;
36856                 input = input.replace(new RegExp('\\' + pad, 'gi'), ''); // use '='
36857                 //input += '';
36858
36859                 do { // unpack four hexets into three octets using index points in b64
36860                   h1 = tab.indexOf(input.charAt(i += 1));
36861                   h2 = tab.indexOf(input.charAt(i += 1));
36862                   h3 = tab.indexOf(input.charAt(i += 1));
36863                   h4 = tab.indexOf(input.charAt(i += 1));
36864
36865                   bits = h1 << 18 | h2 << 12 | h3 << 6 | h4;
36866
36867                   o1 = bits >> 16 & 0xff;
36868                   o2 = bits >> 8 & 0xff;
36869                   o3 = bits & 0xff;
36870                   ac += 1;
36871
36872                   if (h3 === 64) {
36873                     arr[ac] = String.fromCharCode(o1);
36874                   } else if (h4 === 64) {
36875                     arr[ac] = String.fromCharCode(o1, o2);
36876                   } else {
36877                     arr[ac] = String.fromCharCode(o1, o2, o3);
36878                   }
36879                 } while (i < input.length);
36880
36881                 dec = arr.join('');
36882                 dec = (utf8) ? utf8Decode(dec) : dec;
36883
36884                 return dec;
36885               };
36886
36887               // set custom pad string
36888               this.setPad = function(str) {
36889                 pad = str || pad;
36890                 return this;
36891               };
36892               // set custom tab string characters
36893               this.setTab = function(str) {
36894                 tab = str || tab;
36895                 return this;
36896               };
36897               this.setUTF8 = function(bool) {
36898                 if (typeof bool === 'boolean') {
36899                   utf8 = bool;
36900                 }
36901                 return this;
36902               };
36903             },
36904
36905             /**
36906              * CRC-32 calculation
36907              * @member Hashes
36908              * @method CRC32
36909              * @static
36910              * @param {String} str Input String
36911              * @return {String}
36912              */
36913             CRC32: function(str) {
36914               var crc = 0,
36915                 x = 0,
36916                 y = 0,
36917                 table, i, iTop;
36918               str = utf8Encode(str);
36919
36920               table = [
36921                 '00000000 77073096 EE0E612C 990951BA 076DC419 706AF48F E963A535 9E6495A3 0EDB8832 ',
36922                 '79DCB8A4 E0D5E91E 97D2D988 09B64C2B 7EB17CBD E7B82D07 90BF1D91 1DB71064 6AB020F2 F3B97148 ',
36923                 '84BE41DE 1ADAD47D 6DDDE4EB F4D4B551 83D385C7 136C9856 646BA8C0 FD62F97A 8A65C9EC 14015C4F ',
36924                 '63066CD9 FA0F3D63 8D080DF5 3B6E20C8 4C69105E D56041E4 A2677172 3C03E4D1 4B04D447 D20D85FD ',
36925                 'A50AB56B 35B5A8FA 42B2986C DBBBC9D6 ACBCF940 32D86CE3 45DF5C75 DCD60DCF ABD13D59 26D930AC ',
36926                 '51DE003A C8D75180 BFD06116 21B4F4B5 56B3C423 CFBA9599 B8BDA50F 2802B89E 5F058808 C60CD9B2 ',
36927                 'B10BE924 2F6F7C87 58684C11 C1611DAB B6662D3D 76DC4190 01DB7106 98D220BC EFD5102A 71B18589 ',
36928                 '06B6B51F 9FBFE4A5 E8B8D433 7807C9A2 0F00F934 9609A88E E10E9818 7F6A0DBB 086D3D2D 91646C97 ',
36929                 'E6635C01 6B6B51F4 1C6C6162 856530D8 F262004E 6C0695ED 1B01A57B 8208F4C1 F50FC457 65B0D9C6 ',
36930                 '12B7E950 8BBEB8EA FCB9887C 62DD1DDF 15DA2D49 8CD37CF3 FBD44C65 4DB26158 3AB551CE A3BC0074 ',
36931                 'D4BB30E2 4ADFA541 3DD895D7 A4D1C46D D3D6F4FB 4369E96A 346ED9FC AD678846 DA60B8D0 44042D73 ',
36932                 '33031DE5 AA0A4C5F DD0D7CC9 5005713C 270241AA BE0B1010 C90C2086 5768B525 206F85B3 B966D409 ',
36933                 'CE61E49F 5EDEF90E 29D9C998 B0D09822 C7D7A8B4 59B33D17 2EB40D81 B7BD5C3B C0BA6CAD EDB88320 ',
36934                 '9ABFB3B6 03B6E20C 74B1D29A EAD54739 9DD277AF 04DB2615 73DC1683 E3630B12 94643B84 0D6D6A3E ',
36935                 '7A6A5AA8 E40ECF0B 9309FF9D 0A00AE27 7D079EB1 F00F9344 8708A3D2 1E01F268 6906C2FE F762575D ',
36936                 '806567CB 196C3671 6E6B06E7 FED41B76 89D32BE0 10DA7A5A 67DD4ACC F9B9DF6F 8EBEEFF9 17B7BE43 ',
36937                 '60B08ED5 D6D6A3E8 A1D1937E 38D8C2C4 4FDFF252 D1BB67F1 A6BC5767 3FB506DD 48B2364B D80D2BDA ',
36938                 'AF0A1B4C 36034AF6 41047A60 DF60EFC3 A867DF55 316E8EEF 4669BE79 CB61B38C BC66831A 256FD2A0 ',
36939                 '5268E236 CC0C7795 BB0B4703 220216B9 5505262F C5BA3BBE B2BD0B28 2BB45A92 5CB36A04 C2D7FFA7 ',
36940                 'B5D0CF31 2CD99E8B 5BDEAE1D 9B64C2B0 EC63F226 756AA39C 026D930A 9C0906A9 EB0E363F 72076785 ',
36941                 '05005713 95BF4A82 E2B87A14 7BB12BAE 0CB61B38 92D28E9B E5D5BE0D 7CDCEFB7 0BDBDF21 86D3D2D4 ',
36942                 'F1D4E242 68DDB3F8 1FDA836E 81BE16CD F6B9265B 6FB077E1 18B74777 88085AE6 FF0F6A70 66063BCA ',
36943                 '11010B5C 8F659EFF F862AE69 616BFFD3 166CCF45 A00AE278 D70DD2EE 4E048354 3903B3C2 A7672661 ',
36944                 'D06016F7 4969474D 3E6E77DB AED16A4A D9D65ADC 40DF0B66 37D83BF0 A9BCAE53 DEBB9EC5 47B2CF7F ',
36945                 '30B5FFE9 BDBDF21C CABAC28A 53B39330 24B4A3A6 BAD03605 CDD70693 54DE5729 23D967BF B3667A2E ',
36946                 'C4614AB8 5D681B02 2A6F2B94 B40BBE37 C30C8EA1 5A05DF1B 2D02EF8D'
36947               ].join('');
36948
36949               crc = crc ^ (-1);
36950               for (i = 0, iTop = str.length; i < iTop; i += 1) {
36951                 y = (crc ^ str.charCodeAt(i)) & 0xFF;
36952                 x = '0x' + table.substr(y * 9, 8);
36953                 crc = (crc >>> 8) ^ x;
36954               }
36955               // always return a positive number (that's what >>> 0 does)
36956               return (crc ^ (-1)) >>> 0;
36957             },
36958             /**
36959              * @member Hashes
36960              * @class MD5
36961              * @constructor
36962              * @param {Object} [config]
36963              *
36964              * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
36965              * Digest Algorithm, as defined in RFC 1321.
36966              * Version 2.2 Copyright (C) Paul Johnston 1999 - 2009
36967              * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
36968              * See <http://pajhome.org.uk/crypt/md5> for more infHashes.
36969              */
36970             MD5: function(options) {
36971               /**
36972                * Private config properties. You may need to tweak these to be compatible with
36973                * the server-side, but the defaults work in most cases.
36974                * See {@link Hashes.MD5#method-setUpperCase} and {@link Hashes.SHA1#method-setUpperCase}
36975                */
36976               var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false, // hexadecimal output case format. false - lowercase; true - uppercase
36977                 b64pad = (options && typeof options.pad === 'string') ? options.pad : '=', // base-64 pad character. Defaults to '=' for strict RFC compliance
36978                 utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true; // enable/disable utf8 encoding
36979
36980               // privileged (public) methods
36981               this.hex = function(s) {
36982                 return rstr2hex(rstr(s), hexcase);
36983               };
36984               this.b64 = function(s) {
36985                 return rstr2b64(rstr(s), b64pad);
36986               };
36987               this.any = function(s, e) {
36988                 return rstr2any(rstr(s), e);
36989               };
36990               this.raw = function(s) {
36991                 return rstr(s);
36992               };
36993               this.hex_hmac = function(k, d) {
36994                 return rstr2hex(rstr_hmac(k, d), hexcase);
36995               };
36996               this.b64_hmac = function(k, d) {
36997                 return rstr2b64(rstr_hmac(k, d), b64pad);
36998               };
36999               this.any_hmac = function(k, d, e) {
37000                 return rstr2any(rstr_hmac(k, d), e);
37001               };
37002               /**
37003                * Perform a simple self-test to see if the VM is working
37004                * @return {String} Hexadecimal hash sample
37005                */
37006               this.vm_test = function() {
37007                 return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
37008               };
37009               /**
37010                * Enable/disable uppercase hexadecimal returned string
37011                * @param {Boolean}
37012                * @return {Object} this
37013                */
37014               this.setUpperCase = function(a) {
37015                 if (typeof a === 'boolean') {
37016                   hexcase = a;
37017                 }
37018                 return this;
37019               };
37020               /**
37021                * Defines a base64 pad string
37022                * @param {String} Pad
37023                * @return {Object} this
37024                */
37025               this.setPad = function(a) {
37026                 b64pad = a || b64pad;
37027                 return this;
37028               };
37029               /**
37030                * Defines a base64 pad string
37031                * @param {Boolean}
37032                * @return {Object} [this]
37033                */
37034               this.setUTF8 = function(a) {
37035                 if (typeof a === 'boolean') {
37036                   utf8 = a;
37037                 }
37038                 return this;
37039               };
37040
37041               // private methods
37042
37043               /**
37044                * Calculate the MD5 of a raw string
37045                */
37046
37047               function rstr(s) {
37048                 s = (utf8) ? utf8Encode(s) : s;
37049                 return binl2rstr(binl(rstr2binl(s), s.length * 8));
37050               }
37051
37052               /**
37053                * Calculate the HMAC-MD5, of a key and some data (raw strings)
37054                */
37055
37056               function rstr_hmac(key, data) {
37057                 var bkey, ipad, opad, hash, i;
37058
37059                 key = (utf8) ? utf8Encode(key) : key;
37060                 data = (utf8) ? utf8Encode(data) : data;
37061                 bkey = rstr2binl(key);
37062                 if (bkey.length > 16) {
37063                   bkey = binl(bkey, key.length * 8);
37064                 }
37065
37066                 ipad = Array(16), opad = Array(16);
37067                 for (i = 0; i < 16; i += 1) {
37068                   ipad[i] = bkey[i] ^ 0x36363636;
37069                   opad[i] = bkey[i] ^ 0x5C5C5C5C;
37070                 }
37071                 hash = binl(ipad.concat(rstr2binl(data)), 512 + data.length * 8);
37072                 return binl2rstr(binl(opad.concat(hash), 512 + 128));
37073               }
37074
37075               /**
37076                * Calculate the MD5 of an array of little-endian words, and a bit length.
37077                */
37078
37079               function binl(x, len) {
37080                 var i, olda, oldb, oldc, oldd,
37081                   a = 1732584193,
37082                   b = -271733879,
37083                   c = -1732584194,
37084                   d = 271733878;
37085
37086                 /* append padding */
37087                 x[len >> 5] |= 0x80 << ((len) % 32);
37088                 x[(((len + 64) >>> 9) << 4) + 14] = len;
37089
37090                 for (i = 0; i < x.length; i += 16) {
37091                   olda = a;
37092                   oldb = b;
37093                   oldc = c;
37094                   oldd = d;
37095
37096                   a = md5_ff(a, b, c, d, x[i + 0], 7, -680876936);
37097                   d = md5_ff(d, a, b, c, x[i + 1], 12, -389564586);
37098                   c = md5_ff(c, d, a, b, x[i + 2], 17, 606105819);
37099                   b = md5_ff(b, c, d, a, x[i + 3], 22, -1044525330);
37100                   a = md5_ff(a, b, c, d, x[i + 4], 7, -176418897);
37101                   d = md5_ff(d, a, b, c, x[i + 5], 12, 1200080426);
37102                   c = md5_ff(c, d, a, b, x[i + 6], 17, -1473231341);
37103                   b = md5_ff(b, c, d, a, x[i + 7], 22, -45705983);
37104                   a = md5_ff(a, b, c, d, x[i + 8], 7, 1770035416);
37105                   d = md5_ff(d, a, b, c, x[i + 9], 12, -1958414417);
37106                   c = md5_ff(c, d, a, b, x[i + 10], 17, -42063);
37107                   b = md5_ff(b, c, d, a, x[i + 11], 22, -1990404162);
37108                   a = md5_ff(a, b, c, d, x[i + 12], 7, 1804603682);
37109                   d = md5_ff(d, a, b, c, x[i + 13], 12, -40341101);
37110                   c = md5_ff(c, d, a, b, x[i + 14], 17, -1502002290);
37111                   b = md5_ff(b, c, d, a, x[i + 15], 22, 1236535329);
37112
37113                   a = md5_gg(a, b, c, d, x[i + 1], 5, -165796510);
37114                   d = md5_gg(d, a, b, c, x[i + 6], 9, -1069501632);
37115                   c = md5_gg(c, d, a, b, x[i + 11], 14, 643717713);
37116                   b = md5_gg(b, c, d, a, x[i + 0], 20, -373897302);
37117                   a = md5_gg(a, b, c, d, x[i + 5], 5, -701558691);
37118                   d = md5_gg(d, a, b, c, x[i + 10], 9, 38016083);
37119                   c = md5_gg(c, d, a, b, x[i + 15], 14, -660478335);
37120                   b = md5_gg(b, c, d, a, x[i + 4], 20, -405537848);
37121                   a = md5_gg(a, b, c, d, x[i + 9], 5, 568446438);
37122                   d = md5_gg(d, a, b, c, x[i + 14], 9, -1019803690);
37123                   c = md5_gg(c, d, a, b, x[i + 3], 14, -187363961);
37124                   b = md5_gg(b, c, d, a, x[i + 8], 20, 1163531501);
37125                   a = md5_gg(a, b, c, d, x[i + 13], 5, -1444681467);
37126                   d = md5_gg(d, a, b, c, x[i + 2], 9, -51403784);
37127                   c = md5_gg(c, d, a, b, x[i + 7], 14, 1735328473);
37128                   b = md5_gg(b, c, d, a, x[i + 12], 20, -1926607734);
37129
37130                   a = md5_hh(a, b, c, d, x[i + 5], 4, -378558);
37131                   d = md5_hh(d, a, b, c, x[i + 8], 11, -2022574463);
37132                   c = md5_hh(c, d, a, b, x[i + 11], 16, 1839030562);
37133                   b = md5_hh(b, c, d, a, x[i + 14], 23, -35309556);
37134                   a = md5_hh(a, b, c, d, x[i + 1], 4, -1530992060);
37135                   d = md5_hh(d, a, b, c, x[i + 4], 11, 1272893353);
37136                   c = md5_hh(c, d, a, b, x[i + 7], 16, -155497632);
37137                   b = md5_hh(b, c, d, a, x[i + 10], 23, -1094730640);
37138                   a = md5_hh(a, b, c, d, x[i + 13], 4, 681279174);
37139                   d = md5_hh(d, a, b, c, x[i + 0], 11, -358537222);
37140                   c = md5_hh(c, d, a, b, x[i + 3], 16, -722521979);
37141                   b = md5_hh(b, c, d, a, x[i + 6], 23, 76029189);
37142                   a = md5_hh(a, b, c, d, x[i + 9], 4, -640364487);
37143                   d = md5_hh(d, a, b, c, x[i + 12], 11, -421815835);
37144                   c = md5_hh(c, d, a, b, x[i + 15], 16, 530742520);
37145                   b = md5_hh(b, c, d, a, x[i + 2], 23, -995338651);
37146
37147                   a = md5_ii(a, b, c, d, x[i + 0], 6, -198630844);
37148                   d = md5_ii(d, a, b, c, x[i + 7], 10, 1126891415);
37149                   c = md5_ii(c, d, a, b, x[i + 14], 15, -1416354905);
37150                   b = md5_ii(b, c, d, a, x[i + 5], 21, -57434055);
37151                   a = md5_ii(a, b, c, d, x[i + 12], 6, 1700485571);
37152                   d = md5_ii(d, a, b, c, x[i + 3], 10, -1894986606);
37153                   c = md5_ii(c, d, a, b, x[i + 10], 15, -1051523);
37154                   b = md5_ii(b, c, d, a, x[i + 1], 21, -2054922799);
37155                   a = md5_ii(a, b, c, d, x[i + 8], 6, 1873313359);
37156                   d = md5_ii(d, a, b, c, x[i + 15], 10, -30611744);
37157                   c = md5_ii(c, d, a, b, x[i + 6], 15, -1560198380);
37158                   b = md5_ii(b, c, d, a, x[i + 13], 21, 1309151649);
37159                   a = md5_ii(a, b, c, d, x[i + 4], 6, -145523070);
37160                   d = md5_ii(d, a, b, c, x[i + 11], 10, -1120210379);
37161                   c = md5_ii(c, d, a, b, x[i + 2], 15, 718787259);
37162                   b = md5_ii(b, c, d, a, x[i + 9], 21, -343485551);
37163
37164                   a = safe_add(a, olda);
37165                   b = safe_add(b, oldb);
37166                   c = safe_add(c, oldc);
37167                   d = safe_add(d, oldd);
37168                 }
37169                 return Array(a, b, c, d);
37170               }
37171
37172               /**
37173                * These functions implement the four basic operations the algorithm uses.
37174                */
37175
37176               function md5_cmn(q, a, b, x, s, t) {
37177                 return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s), b);
37178               }
37179
37180               function md5_ff(a, b, c, d, x, s, t) {
37181                 return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
37182               }
37183
37184               function md5_gg(a, b, c, d, x, s, t) {
37185                 return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
37186               }
37187
37188               function md5_hh(a, b, c, d, x, s, t) {
37189                 return md5_cmn(b ^ c ^ d, a, b, x, s, t);
37190               }
37191
37192               function md5_ii(a, b, c, d, x, s, t) {
37193                 return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
37194               }
37195             },
37196             /**
37197              * @member Hashes
37198              * @class Hashes.SHA1
37199              * @param {Object} [config]
37200              * @constructor
37201              *
37202              * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined in FIPS 180-1
37203              * Version 2.2 Copyright Paul Johnston 2000 - 2009.
37204              * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
37205              * See http://pajhome.org.uk/crypt/md5 for details.
37206              */
37207             SHA1: function(options) {
37208               /**
37209                * Private config properties. You may need to tweak these to be compatible with
37210                * the server-side, but the defaults work in most cases.
37211                * See {@link Hashes.MD5#method-setUpperCase} and {@link Hashes.SHA1#method-setUpperCase}
37212                */
37213               var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false, // hexadecimal output case format. false - lowercase; true - uppercase
37214                 b64pad = (options && typeof options.pad === 'string') ? options.pad : '=', // base-64 pad character. Defaults to '=' for strict RFC compliance
37215                 utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true; // enable/disable utf8 encoding
37216
37217               // public methods
37218               this.hex = function(s) {
37219                 return rstr2hex(rstr(s), hexcase);
37220               };
37221               this.b64 = function(s) {
37222                 return rstr2b64(rstr(s), b64pad);
37223               };
37224               this.any = function(s, e) {
37225                 return rstr2any(rstr(s), e);
37226               };
37227               this.raw = function(s) {
37228                 return rstr(s);
37229               };
37230               this.hex_hmac = function(k, d) {
37231                 return rstr2hex(rstr_hmac(k, d));
37232               };
37233               this.b64_hmac = function(k, d) {
37234                 return rstr2b64(rstr_hmac(k, d), b64pad);
37235               };
37236               this.any_hmac = function(k, d, e) {
37237                 return rstr2any(rstr_hmac(k, d), e);
37238               };
37239               /**
37240                * Perform a simple self-test to see if the VM is working
37241                * @return {String} Hexadecimal hash sample
37242                * @public
37243                */
37244               this.vm_test = function() {
37245                 return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
37246               };
37247               /**
37248                * @description Enable/disable uppercase hexadecimal returned string
37249                * @param {boolean}
37250                * @return {Object} this
37251                * @public
37252                */
37253               this.setUpperCase = function(a) {
37254                 if (typeof a === 'boolean') {
37255                   hexcase = a;
37256                 }
37257                 return this;
37258               };
37259               /**
37260                * @description Defines a base64 pad string
37261                * @param {string} Pad
37262                * @return {Object} this
37263                * @public
37264                */
37265               this.setPad = function(a) {
37266                 b64pad = a || b64pad;
37267                 return this;
37268               };
37269               /**
37270                * @description Defines a base64 pad string
37271                * @param {boolean}
37272                * @return {Object} this
37273                * @public
37274                */
37275               this.setUTF8 = function(a) {
37276                 if (typeof a === 'boolean') {
37277                   utf8 = a;
37278                 }
37279                 return this;
37280               };
37281
37282               // private methods
37283
37284               /**
37285                * Calculate the SHA-512 of a raw string
37286                */
37287
37288               function rstr(s) {
37289                 s = (utf8) ? utf8Encode(s) : s;
37290                 return binb2rstr(binb(rstr2binb(s), s.length * 8));
37291               }
37292
37293               /**
37294                * Calculate the HMAC-SHA1 of a key and some data (raw strings)
37295                */
37296
37297               function rstr_hmac(key, data) {
37298                 var bkey, ipad, opad, i, hash;
37299                 key = (utf8) ? utf8Encode(key) : key;
37300                 data = (utf8) ? utf8Encode(data) : data;
37301                 bkey = rstr2binb(key);
37302
37303                 if (bkey.length > 16) {
37304                   bkey = binb(bkey, key.length * 8);
37305                 }
37306                 ipad = Array(16), opad = Array(16);
37307                 for (i = 0; i < 16; i += 1) {
37308                   ipad[i] = bkey[i] ^ 0x36363636;
37309                   opad[i] = bkey[i] ^ 0x5C5C5C5C;
37310                 }
37311                 hash = binb(ipad.concat(rstr2binb(data)), 512 + data.length * 8);
37312                 return binb2rstr(binb(opad.concat(hash), 512 + 160));
37313               }
37314
37315               /**
37316                * Calculate the SHA-1 of an array of big-endian words, and a bit length
37317                */
37318
37319               function binb(x, len) {
37320                 var i, j, t, olda, oldb, oldc, oldd, olde,
37321                   w = Array(80),
37322                   a = 1732584193,
37323                   b = -271733879,
37324                   c = -1732584194,
37325                   d = 271733878,
37326                   e = -1009589776;
37327
37328                 /* append padding */
37329                 x[len >> 5] |= 0x80 << (24 - len % 32);
37330                 x[((len + 64 >> 9) << 4) + 15] = len;
37331
37332                 for (i = 0; i < x.length; i += 16) {
37333                   olda = a;
37334                   oldb = b;
37335                   oldc = c;
37336                   oldd = d;
37337                   olde = e;
37338
37339                   for (j = 0; j < 80; j += 1) {
37340                     if (j < 16) {
37341                       w[j] = x[i + j];
37342                     } else {
37343                       w[j] = bit_rol(w[j - 3] ^ w[j - 8] ^ w[j - 14] ^ w[j - 16], 1);
37344                     }
37345                     t = safe_add(safe_add(bit_rol(a, 5), sha1_ft(j, b, c, d)),
37346                       safe_add(safe_add(e, w[j]), sha1_kt(j)));
37347                     e = d;
37348                     d = c;
37349                     c = bit_rol(b, 30);
37350                     b = a;
37351                     a = t;
37352                   }
37353
37354                   a = safe_add(a, olda);
37355                   b = safe_add(b, oldb);
37356                   c = safe_add(c, oldc);
37357                   d = safe_add(d, oldd);
37358                   e = safe_add(e, olde);
37359                 }
37360                 return Array(a, b, c, d, e);
37361               }
37362
37363               /**
37364                * Perform the appropriate triplet combination function for the current
37365                * iteration
37366                */
37367
37368               function sha1_ft(t, b, c, d) {
37369                 if (t < 20) {
37370                   return (b & c) | ((~b) & d);
37371                 }
37372                 if (t < 40) {
37373                   return b ^ c ^ d;
37374                 }
37375                 if (t < 60) {
37376                   return (b & c) | (b & d) | (c & d);
37377                 }
37378                 return b ^ c ^ d;
37379               }
37380
37381               /**
37382                * Determine the appropriate additive constant for the current iteration
37383                */
37384
37385               function sha1_kt(t) {
37386                 return (t < 20) ? 1518500249 : (t < 40) ? 1859775393 :
37387                   (t < 60) ? -1894007588 : -899497514;
37388               }
37389             },
37390             /**
37391              * @class Hashes.SHA256
37392              * @param {config}
37393              *
37394              * A JavaScript implementation of the Secure Hash Algorithm, SHA-256, as defined in FIPS 180-2
37395              * Version 2.2 Copyright Angel Marin, Paul Johnston 2000 - 2009.
37396              * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
37397              * See http://pajhome.org.uk/crypt/md5 for details.
37398              * Also http://anmar.eu.org/projects/jssha2/
37399              */
37400             SHA256: function(options) {
37401               /**
37402                * Private properties configuration variables. You may need to tweak these to be compatible with
37403                * the server-side, but the defaults work in most cases.
37404                * @see this.setUpperCase() method
37405                * @see this.setPad() method
37406                */
37407               var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false, // hexadecimal output case format. false - lowercase; true - uppercase  */
37408                 b64pad = (options && typeof options.pad === 'string') ? options.pad : '=',
37409                 /* base-64 pad character. Default '=' for strict RFC compliance   */
37410                 utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true,
37411                 /* enable/disable utf8 encoding */
37412                 sha256_K;
37413
37414               /* privileged (public) methods */
37415               this.hex = function(s) {
37416                 return rstr2hex(rstr(s, utf8));
37417               };
37418               this.b64 = function(s) {
37419                 return rstr2b64(rstr(s, utf8), b64pad);
37420               };
37421               this.any = function(s, e) {
37422                 return rstr2any(rstr(s, utf8), e);
37423               };
37424               this.raw = function(s) {
37425                 return rstr(s, utf8);
37426               };
37427               this.hex_hmac = function(k, d) {
37428                 return rstr2hex(rstr_hmac(k, d));
37429               };
37430               this.b64_hmac = function(k, d) {
37431                 return rstr2b64(rstr_hmac(k, d), b64pad);
37432               };
37433               this.any_hmac = function(k, d, e) {
37434                 return rstr2any(rstr_hmac(k, d), e);
37435               };
37436               /**
37437                * Perform a simple self-test to see if the VM is working
37438                * @return {String} Hexadecimal hash sample
37439                * @public
37440                */
37441               this.vm_test = function() {
37442                 return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
37443               };
37444               /**
37445                * Enable/disable uppercase hexadecimal returned string
37446                * @param {boolean}
37447                * @return {Object} this
37448                * @public
37449                */
37450               this.setUpperCase = function(a) {
37451                 if (typeof a === 'boolean') {
37452                   hexcase = a;
37453                 }
37454                 return this;
37455               };
37456               /**
37457                * @description Defines a base64 pad string
37458                * @param {string} Pad
37459                * @return {Object} this
37460                * @public
37461                */
37462               this.setPad = function(a) {
37463                 b64pad = a || b64pad;
37464                 return this;
37465               };
37466               /**
37467                * Defines a base64 pad string
37468                * @param {boolean}
37469                * @return {Object} this
37470                * @public
37471                */
37472               this.setUTF8 = function(a) {
37473                 if (typeof a === 'boolean') {
37474                   utf8 = a;
37475                 }
37476                 return this;
37477               };
37478
37479               // private methods
37480
37481               /**
37482                * Calculate the SHA-512 of a raw string
37483                */
37484
37485               function rstr(s, utf8) {
37486                 s = (utf8) ? utf8Encode(s) : s;
37487                 return binb2rstr(binb(rstr2binb(s), s.length * 8));
37488               }
37489
37490               /**
37491                * Calculate the HMAC-sha256 of a key and some data (raw strings)
37492                */
37493
37494               function rstr_hmac(key, data) {
37495                 key = (utf8) ? utf8Encode(key) : key;
37496                 data = (utf8) ? utf8Encode(data) : data;
37497                 var hash, i = 0,
37498                   bkey = rstr2binb(key),
37499                   ipad = Array(16),
37500                   opad = Array(16);
37501
37502                 if (bkey.length > 16) {
37503                   bkey = binb(bkey, key.length * 8);
37504                 }
37505
37506                 for (; i < 16; i += 1) {
37507                   ipad[i] = bkey[i] ^ 0x36363636;
37508                   opad[i] = bkey[i] ^ 0x5C5C5C5C;
37509                 }
37510
37511                 hash = binb(ipad.concat(rstr2binb(data)), 512 + data.length * 8);
37512                 return binb2rstr(binb(opad.concat(hash), 512 + 256));
37513               }
37514
37515               /*
37516                * Main sha256 function, with its support functions
37517                */
37518
37519               function sha256_S(X, n) {
37520                 return (X >>> n) | (X << (32 - n));
37521               }
37522
37523               function sha256_R(X, n) {
37524                 return (X >>> n);
37525               }
37526
37527               function sha256_Ch(x, y, z) {
37528                 return ((x & y) ^ ((~x) & z));
37529               }
37530
37531               function sha256_Maj(x, y, z) {
37532                 return ((x & y) ^ (x & z) ^ (y & z));
37533               }
37534
37535               function sha256_Sigma0256(x) {
37536                 return (sha256_S(x, 2) ^ sha256_S(x, 13) ^ sha256_S(x, 22));
37537               }
37538
37539               function sha256_Sigma1256(x) {
37540                 return (sha256_S(x, 6) ^ sha256_S(x, 11) ^ sha256_S(x, 25));
37541               }
37542
37543               function sha256_Gamma0256(x) {
37544                 return (sha256_S(x, 7) ^ sha256_S(x, 18) ^ sha256_R(x, 3));
37545               }
37546
37547               function sha256_Gamma1256(x) {
37548                 return (sha256_S(x, 17) ^ sha256_S(x, 19) ^ sha256_R(x, 10));
37549               }
37550
37551               sha256_K = [
37552                 1116352408, 1899447441, -1245643825, -373957723, 961987163, 1508970993, -1841331548, -1424204075, -670586216, 310598401, 607225278, 1426881987,
37553                 1925078388, -2132889090, -1680079193, -1046744716, -459576895, -272742522,
37554                 264347078, 604807628, 770255983, 1249150122, 1555081692, 1996064986, -1740746414, -1473132947, -1341970488, -1084653625, -958395405, -710438585,
37555                 113926993, 338241895, 666307205, 773529912, 1294757372, 1396182291,
37556                 1695183700, 1986661051, -2117940946, -1838011259, -1564481375, -1474664885, -1035236496, -949202525, -778901479, -694614492, -200395387, 275423344,
37557                 430227734, 506948616, 659060556, 883997877, 958139571, 1322822218,
37558                 1537002063, 1747873779, 1955562222, 2024104815, -2067236844, -1933114872, -1866530822, -1538233109, -1090935817, -965641998
37559               ];
37560
37561               function binb(m, l) {
37562                 var HASH = [1779033703, -1150833019, 1013904242, -1521486534,
37563                   1359893119, -1694144372, 528734635, 1541459225
37564                 ];
37565                 var W = new Array(64);
37566                 var a, b, c, d, e, f, g, h;
37567                 var i, j, T1, T2;
37568
37569                 /* append padding */
37570                 m[l >> 5] |= 0x80 << (24 - l % 32);
37571                 m[((l + 64 >> 9) << 4) + 15] = l;
37572
37573                 for (i = 0; i < m.length; i += 16) {
37574                   a = HASH[0];
37575                   b = HASH[1];
37576                   c = HASH[2];
37577                   d = HASH[3];
37578                   e = HASH[4];
37579                   f = HASH[5];
37580                   g = HASH[6];
37581                   h = HASH[7];
37582
37583                   for (j = 0; j < 64; j += 1) {
37584                     if (j < 16) {
37585                       W[j] = m[j + i];
37586                     } else {
37587                       W[j] = safe_add(safe_add(safe_add(sha256_Gamma1256(W[j - 2]), W[j - 7]),
37588                         sha256_Gamma0256(W[j - 15])), W[j - 16]);
37589                     }
37590
37591                     T1 = safe_add(safe_add(safe_add(safe_add(h, sha256_Sigma1256(e)), sha256_Ch(e, f, g)),
37592                       sha256_K[j]), W[j]);
37593                     T2 = safe_add(sha256_Sigma0256(a), sha256_Maj(a, b, c));
37594                     h = g;
37595                     g = f;
37596                     f = e;
37597                     e = safe_add(d, T1);
37598                     d = c;
37599                     c = b;
37600                     b = a;
37601                     a = safe_add(T1, T2);
37602                   }
37603
37604                   HASH[0] = safe_add(a, HASH[0]);
37605                   HASH[1] = safe_add(b, HASH[1]);
37606                   HASH[2] = safe_add(c, HASH[2]);
37607                   HASH[3] = safe_add(d, HASH[3]);
37608                   HASH[4] = safe_add(e, HASH[4]);
37609                   HASH[5] = safe_add(f, HASH[5]);
37610                   HASH[6] = safe_add(g, HASH[6]);
37611                   HASH[7] = safe_add(h, HASH[7]);
37612                 }
37613                 return HASH;
37614               }
37615
37616             },
37617
37618             /**
37619              * @class Hashes.SHA512
37620              * @param {config}
37621              *
37622              * A JavaScript implementation of the Secure Hash Algorithm, SHA-512, as defined in FIPS 180-2
37623              * Version 2.2 Copyright Anonymous Contributor, Paul Johnston 2000 - 2009.
37624              * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
37625              * See http://pajhome.org.uk/crypt/md5 for details.
37626              */
37627             SHA512: function(options) {
37628               /**
37629                * Private properties configuration variables. You may need to tweak these to be compatible with
37630                * the server-side, but the defaults work in most cases.
37631                * @see this.setUpperCase() method
37632                * @see this.setPad() method
37633                */
37634               var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false,
37635                 /* hexadecimal output case format. false - lowercase; true - uppercase  */
37636                 b64pad = (options && typeof options.pad === 'string') ? options.pad : '=',
37637                 /* base-64 pad character. Default '=' for strict RFC compliance   */
37638                 utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true,
37639                 /* enable/disable utf8 encoding */
37640                 sha512_k;
37641
37642               /* privileged (public) methods */
37643               this.hex = function(s) {
37644                 return rstr2hex(rstr(s));
37645               };
37646               this.b64 = function(s) {
37647                 return rstr2b64(rstr(s), b64pad);
37648               };
37649               this.any = function(s, e) {
37650                 return rstr2any(rstr(s), e);
37651               };
37652               this.raw = function(s) {
37653                 return rstr(s);
37654               };
37655               this.hex_hmac = function(k, d) {
37656                 return rstr2hex(rstr_hmac(k, d));
37657               };
37658               this.b64_hmac = function(k, d) {
37659                 return rstr2b64(rstr_hmac(k, d), b64pad);
37660               };
37661               this.any_hmac = function(k, d, e) {
37662                 return rstr2any(rstr_hmac(k, d), e);
37663               };
37664               /**
37665                * Perform a simple self-test to see if the VM is working
37666                * @return {String} Hexadecimal hash sample
37667                * @public
37668                */
37669               this.vm_test = function() {
37670                 return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
37671               };
37672               /**
37673                * @description Enable/disable uppercase hexadecimal returned string
37674                * @param {boolean}
37675                * @return {Object} this
37676                * @public
37677                */
37678               this.setUpperCase = function(a) {
37679                 if (typeof a === 'boolean') {
37680                   hexcase = a;
37681                 }
37682                 return this;
37683               };
37684               /**
37685                * @description Defines a base64 pad string
37686                * @param {string} Pad
37687                * @return {Object} this
37688                * @public
37689                */
37690               this.setPad = function(a) {
37691                 b64pad = a || b64pad;
37692                 return this;
37693               };
37694               /**
37695                * @description Defines a base64 pad string
37696                * @param {boolean}
37697                * @return {Object} this
37698                * @public
37699                */
37700               this.setUTF8 = function(a) {
37701                 if (typeof a === 'boolean') {
37702                   utf8 = a;
37703                 }
37704                 return this;
37705               };
37706
37707               /* private methods */
37708
37709               /**
37710                * Calculate the SHA-512 of a raw string
37711                */
37712
37713               function rstr(s) {
37714                 s = (utf8) ? utf8Encode(s) : s;
37715                 return binb2rstr(binb(rstr2binb(s), s.length * 8));
37716               }
37717               /*
37718                * Calculate the HMAC-SHA-512 of a key and some data (raw strings)
37719                */
37720
37721               function rstr_hmac(key, data) {
37722                 key = (utf8) ? utf8Encode(key) : key;
37723                 data = (utf8) ? utf8Encode(data) : data;
37724
37725                 var hash, i = 0,
37726                   bkey = rstr2binb(key),
37727                   ipad = Array(32),
37728                   opad = Array(32);
37729
37730                 if (bkey.length > 32) {
37731                   bkey = binb(bkey, key.length * 8);
37732                 }
37733
37734                 for (; i < 32; i += 1) {
37735                   ipad[i] = bkey[i] ^ 0x36363636;
37736                   opad[i] = bkey[i] ^ 0x5C5C5C5C;
37737                 }
37738
37739                 hash = binb(ipad.concat(rstr2binb(data)), 1024 + data.length * 8);
37740                 return binb2rstr(binb(opad.concat(hash), 1024 + 512));
37741               }
37742
37743               /**
37744                * Calculate the SHA-512 of an array of big-endian dwords, and a bit length
37745                */
37746
37747               function binb(x, len) {
37748                 var j, i, l,
37749                   W = new Array(80),
37750                   hash = new Array(16),
37751                   //Initial hash values
37752                   H = [
37753                     new int64(0x6a09e667, -205731576),
37754                     new int64(-1150833019, -2067093701),
37755                     new int64(0x3c6ef372, -23791573),
37756                     new int64(-1521486534, 0x5f1d36f1),
37757                     new int64(0x510e527f, -1377402159),
37758                     new int64(-1694144372, 0x2b3e6c1f),
37759                     new int64(0x1f83d9ab, -79577749),
37760                     new int64(0x5be0cd19, 0x137e2179)
37761                   ],
37762                   T1 = new int64(0, 0),
37763                   T2 = new int64(0, 0),
37764                   a = new int64(0, 0),
37765                   b = new int64(0, 0),
37766                   c = new int64(0, 0),
37767                   d = new int64(0, 0),
37768                   e = new int64(0, 0),
37769                   f = new int64(0, 0),
37770                   g = new int64(0, 0),
37771                   h = new int64(0, 0),
37772                   //Temporary variables not specified by the document
37773                   s0 = new int64(0, 0),
37774                   s1 = new int64(0, 0),
37775                   Ch = new int64(0, 0),
37776                   Maj = new int64(0, 0),
37777                   r1 = new int64(0, 0),
37778                   r2 = new int64(0, 0),
37779                   r3 = new int64(0, 0);
37780
37781                 if (sha512_k === undefined) {
37782                   //SHA512 constants
37783                   sha512_k = [
37784                     new int64(0x428a2f98, -685199838), new int64(0x71374491, 0x23ef65cd),
37785                     new int64(-1245643825, -330482897), new int64(-373957723, -2121671748),
37786                     new int64(0x3956c25b, -213338824), new int64(0x59f111f1, -1241133031),
37787                     new int64(-1841331548, -1357295717), new int64(-1424204075, -630357736),
37788                     new int64(-670586216, -1560083902), new int64(0x12835b01, 0x45706fbe),
37789                     new int64(0x243185be, 0x4ee4b28c), new int64(0x550c7dc3, -704662302),
37790                     new int64(0x72be5d74, -226784913), new int64(-2132889090, 0x3b1696b1),
37791                     new int64(-1680079193, 0x25c71235), new int64(-1046744716, -815192428),
37792                     new int64(-459576895, -1628353838), new int64(-272742522, 0x384f25e3),
37793                     new int64(0xfc19dc6, -1953704523), new int64(0x240ca1cc, 0x77ac9c65),
37794                     new int64(0x2de92c6f, 0x592b0275), new int64(0x4a7484aa, 0x6ea6e483),
37795                     new int64(0x5cb0a9dc, -1119749164), new int64(0x76f988da, -2096016459),
37796                     new int64(-1740746414, -295247957), new int64(-1473132947, 0x2db43210),
37797                     new int64(-1341970488, -1728372417), new int64(-1084653625, -1091629340),
37798                     new int64(-958395405, 0x3da88fc2), new int64(-710438585, -1828018395),
37799                     new int64(0x6ca6351, -536640913), new int64(0x14292967, 0xa0e6e70),
37800                     new int64(0x27b70a85, 0x46d22ffc), new int64(0x2e1b2138, 0x5c26c926),
37801                     new int64(0x4d2c6dfc, 0x5ac42aed), new int64(0x53380d13, -1651133473),
37802                     new int64(0x650a7354, -1951439906), new int64(0x766a0abb, 0x3c77b2a8),
37803                     new int64(-2117940946, 0x47edaee6), new int64(-1838011259, 0x1482353b),
37804                     new int64(-1564481375, 0x4cf10364), new int64(-1474664885, -1136513023),
37805                     new int64(-1035236496, -789014639), new int64(-949202525, 0x654be30),
37806                     new int64(-778901479, -688958952), new int64(-694614492, 0x5565a910),
37807                     new int64(-200395387, 0x5771202a), new int64(0x106aa070, 0x32bbd1b8),
37808                     new int64(0x19a4c116, -1194143544), new int64(0x1e376c08, 0x5141ab53),
37809                     new int64(0x2748774c, -544281703), new int64(0x34b0bcb5, -509917016),
37810                     new int64(0x391c0cb3, -976659869), new int64(0x4ed8aa4a, -482243893),
37811                     new int64(0x5b9cca4f, 0x7763e373), new int64(0x682e6ff3, -692930397),
37812                     new int64(0x748f82ee, 0x5defb2fc), new int64(0x78a5636f, 0x43172f60),
37813                     new int64(-2067236844, -1578062990), new int64(-1933114872, 0x1a6439ec),
37814                     new int64(-1866530822, 0x23631e28), new int64(-1538233109, -561857047),
37815                     new int64(-1090935817, -1295615723), new int64(-965641998, -479046869),
37816                     new int64(-903397682, -366583396), new int64(-779700025, 0x21c0c207),
37817                     new int64(-354779690, -840897762), new int64(-176337025, -294727304),
37818                     new int64(0x6f067aa, 0x72176fba), new int64(0xa637dc5, -1563912026),
37819                     new int64(0x113f9804, -1090974290), new int64(0x1b710b35, 0x131c471b),
37820                     new int64(0x28db77f5, 0x23047d84), new int64(0x32caab7b, 0x40c72493),
37821                     new int64(0x3c9ebe0a, 0x15c9bebc), new int64(0x431d67c4, -1676669620),
37822                     new int64(0x4cc5d4be, -885112138), new int64(0x597f299c, -60457430),
37823                     new int64(0x5fcb6fab, 0x3ad6faec), new int64(0x6c44198c, 0x4a475817)
37824                   ];
37825                 }
37826
37827                 for (i = 0; i < 80; i += 1) {
37828                   W[i] = new int64(0, 0);
37829                 }
37830
37831                 // append padding to the source string. The format is described in the FIPS.
37832                 x[len >> 5] |= 0x80 << (24 - (len & 0x1f));
37833                 x[((len + 128 >> 10) << 5) + 31] = len;
37834                 l = x.length;
37835                 for (i = 0; i < l; i += 32) { //32 dwords is the block size
37836                   int64copy(a, H[0]);
37837                   int64copy(b, H[1]);
37838                   int64copy(c, H[2]);
37839                   int64copy(d, H[3]);
37840                   int64copy(e, H[4]);
37841                   int64copy(f, H[5]);
37842                   int64copy(g, H[6]);
37843                   int64copy(h, H[7]);
37844
37845                   for (j = 0; j < 16; j += 1) {
37846                     W[j].h = x[i + 2 * j];
37847                     W[j].l = x[i + 2 * j + 1];
37848                   }
37849
37850                   for (j = 16; j < 80; j += 1) {
37851                     //sigma1
37852                     int64rrot(r1, W[j - 2], 19);
37853                     int64revrrot(r2, W[j - 2], 29);
37854                     int64shr(r3, W[j - 2], 6);
37855                     s1.l = r1.l ^ r2.l ^ r3.l;
37856                     s1.h = r1.h ^ r2.h ^ r3.h;
37857                     //sigma0
37858                     int64rrot(r1, W[j - 15], 1);
37859                     int64rrot(r2, W[j - 15], 8);
37860                     int64shr(r3, W[j - 15], 7);
37861                     s0.l = r1.l ^ r2.l ^ r3.l;
37862                     s0.h = r1.h ^ r2.h ^ r3.h;
37863
37864                     int64add4(W[j], s1, W[j - 7], s0, W[j - 16]);
37865                   }
37866
37867                   for (j = 0; j < 80; j += 1) {
37868                     //Ch
37869                     Ch.l = (e.l & f.l) ^ (~e.l & g.l);
37870                     Ch.h = (e.h & f.h) ^ (~e.h & g.h);
37871
37872                     //Sigma1
37873                     int64rrot(r1, e, 14);
37874                     int64rrot(r2, e, 18);
37875                     int64revrrot(r3, e, 9);
37876                     s1.l = r1.l ^ r2.l ^ r3.l;
37877                     s1.h = r1.h ^ r2.h ^ r3.h;
37878
37879                     //Sigma0
37880                     int64rrot(r1, a, 28);
37881                     int64revrrot(r2, a, 2);
37882                     int64revrrot(r3, a, 7);
37883                     s0.l = r1.l ^ r2.l ^ r3.l;
37884                     s0.h = r1.h ^ r2.h ^ r3.h;
37885
37886                     //Maj
37887                     Maj.l = (a.l & b.l) ^ (a.l & c.l) ^ (b.l & c.l);
37888                     Maj.h = (a.h & b.h) ^ (a.h & c.h) ^ (b.h & c.h);
37889
37890                     int64add5(T1, h, s1, Ch, sha512_k[j], W[j]);
37891                     int64add(T2, s0, Maj);
37892
37893                     int64copy(h, g);
37894                     int64copy(g, f);
37895                     int64copy(f, e);
37896                     int64add(e, d, T1);
37897                     int64copy(d, c);
37898                     int64copy(c, b);
37899                     int64copy(b, a);
37900                     int64add(a, T1, T2);
37901                   }
37902                   int64add(H[0], H[0], a);
37903                   int64add(H[1], H[1], b);
37904                   int64add(H[2], H[2], c);
37905                   int64add(H[3], H[3], d);
37906                   int64add(H[4], H[4], e);
37907                   int64add(H[5], H[5], f);
37908                   int64add(H[6], H[6], g);
37909                   int64add(H[7], H[7], h);
37910                 }
37911
37912                 //represent the hash as an array of 32-bit dwords
37913                 for (i = 0; i < 8; i += 1) {
37914                   hash[2 * i] = H[i].h;
37915                   hash[2 * i + 1] = H[i].l;
37916                 }
37917                 return hash;
37918               }
37919
37920               //A constructor for 64-bit numbers
37921
37922               function int64(h, l) {
37923                 this.h = h;
37924                 this.l = l;
37925                 //this.toString = int64toString;
37926               }
37927
37928               //Copies src into dst, assuming both are 64-bit numbers
37929
37930               function int64copy(dst, src) {
37931                 dst.h = src.h;
37932                 dst.l = src.l;
37933               }
37934
37935               //Right-rotates a 64-bit number by shift
37936               //Won't handle cases of shift>=32
37937               //The function revrrot() is for that
37938
37939               function int64rrot(dst, x, shift) {
37940                 dst.l = (x.l >>> shift) | (x.h << (32 - shift));
37941                 dst.h = (x.h >>> shift) | (x.l << (32 - shift));
37942               }
37943
37944               //Reverses the dwords of the source and then rotates right by shift.
37945               //This is equivalent to rotation by 32+shift
37946
37947               function int64revrrot(dst, x, shift) {
37948                 dst.l = (x.h >>> shift) | (x.l << (32 - shift));
37949                 dst.h = (x.l >>> shift) | (x.h << (32 - shift));
37950               }
37951
37952               //Bitwise-shifts right a 64-bit number by shift
37953               //Won't handle shift>=32, but it's never needed in SHA512
37954
37955               function int64shr(dst, x, shift) {
37956                 dst.l = (x.l >>> shift) | (x.h << (32 - shift));
37957                 dst.h = (x.h >>> shift);
37958               }
37959
37960               //Adds two 64-bit numbers
37961               //Like the original implementation, does not rely on 32-bit operations
37962
37963               function int64add(dst, x, y) {
37964                 var w0 = (x.l & 0xffff) + (y.l & 0xffff);
37965                 var w1 = (x.l >>> 16) + (y.l >>> 16) + (w0 >>> 16);
37966                 var w2 = (x.h & 0xffff) + (y.h & 0xffff) + (w1 >>> 16);
37967                 var w3 = (x.h >>> 16) + (y.h >>> 16) + (w2 >>> 16);
37968                 dst.l = (w0 & 0xffff) | (w1 << 16);
37969                 dst.h = (w2 & 0xffff) | (w3 << 16);
37970               }
37971
37972               //Same, except with 4 addends. Works faster than adding them one by one.
37973
37974               function int64add4(dst, a, b, c, d) {
37975                 var w0 = (a.l & 0xffff) + (b.l & 0xffff) + (c.l & 0xffff) + (d.l & 0xffff);
37976                 var w1 = (a.l >>> 16) + (b.l >>> 16) + (c.l >>> 16) + (d.l >>> 16) + (w0 >>> 16);
37977                 var w2 = (a.h & 0xffff) + (b.h & 0xffff) + (c.h & 0xffff) + (d.h & 0xffff) + (w1 >>> 16);
37978                 var w3 = (a.h >>> 16) + (b.h >>> 16) + (c.h >>> 16) + (d.h >>> 16) + (w2 >>> 16);
37979                 dst.l = (w0 & 0xffff) | (w1 << 16);
37980                 dst.h = (w2 & 0xffff) | (w3 << 16);
37981               }
37982
37983               //Same, except with 5 addends
37984
37985               function int64add5(dst, a, b, c, d, e) {
37986                 var w0 = (a.l & 0xffff) + (b.l & 0xffff) + (c.l & 0xffff) + (d.l & 0xffff) + (e.l & 0xffff),
37987                   w1 = (a.l >>> 16) + (b.l >>> 16) + (c.l >>> 16) + (d.l >>> 16) + (e.l >>> 16) + (w0 >>> 16),
37988                   w2 = (a.h & 0xffff) + (b.h & 0xffff) + (c.h & 0xffff) + (d.h & 0xffff) + (e.h & 0xffff) + (w1 >>> 16),
37989                   w3 = (a.h >>> 16) + (b.h >>> 16) + (c.h >>> 16) + (d.h >>> 16) + (e.h >>> 16) + (w2 >>> 16);
37990                 dst.l = (w0 & 0xffff) | (w1 << 16);
37991                 dst.h = (w2 & 0xffff) | (w3 << 16);
37992               }
37993             },
37994             /**
37995              * @class Hashes.RMD160
37996              * @constructor
37997              * @param {Object} [config]
37998              *
37999              * A JavaScript implementation of the RIPEMD-160 Algorithm
38000              * Version 2.2 Copyright Jeremy Lin, Paul Johnston 2000 - 2009.
38001              * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
38002              * See http://pajhome.org.uk/crypt/md5 for details.
38003              * Also http://www.ocf.berkeley.edu/~jjlin/jsotp/
38004              */
38005             RMD160: function(options) {
38006               /**
38007                * Private properties configuration variables. You may need to tweak these to be compatible with
38008                * the server-side, but the defaults work in most cases.
38009                * @see this.setUpperCase() method
38010                * @see this.setPad() method
38011                */
38012               var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false,
38013                 /* hexadecimal output case format. false - lowercase; true - uppercase  */
38014                 b64pad = (options && typeof options.pad === 'string') ? options.pa : '=',
38015                 /* base-64 pad character. Default '=' for strict RFC compliance   */
38016                 utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true,
38017                 /* enable/disable utf8 encoding */
38018                 rmd160_r1 = [
38019                   0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
38020                   7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8,
38021                   3, 10, 14, 4, 9, 15, 8, 1, 2, 7, 0, 6, 13, 11, 5, 12,
38022                   1, 9, 11, 10, 0, 8, 12, 4, 13, 3, 7, 15, 14, 5, 6, 2,
38023                   4, 0, 5, 9, 7, 12, 2, 10, 14, 1, 3, 8, 11, 6, 15, 13
38024                 ],
38025                 rmd160_r2 = [
38026                   5, 14, 7, 0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12,
38027                   6, 11, 3, 7, 0, 13, 5, 10, 14, 15, 8, 12, 4, 9, 1, 2,
38028                   15, 5, 1, 3, 7, 14, 6, 9, 11, 8, 12, 2, 10, 0, 4, 13,
38029                   8, 6, 4, 1, 3, 11, 15, 0, 5, 12, 2, 13, 9, 7, 10, 14,
38030                   12, 15, 10, 4, 1, 5, 8, 7, 6, 2, 13, 14, 0, 3, 9, 11
38031                 ],
38032                 rmd160_s1 = [
38033                   11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8,
38034                   7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15, 9, 11, 7, 13, 12,
38035                   11, 13, 6, 7, 14, 9, 13, 15, 14, 8, 13, 6, 5, 12, 7, 5,
38036                   11, 12, 14, 15, 14, 15, 9, 8, 9, 14, 5, 6, 8, 6, 5, 12,
38037                   9, 15, 5, 11, 6, 8, 13, 12, 5, 12, 13, 14, 11, 8, 5, 6
38038                 ],
38039                 rmd160_s2 = [
38040                   8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6,
38041                   9, 13, 15, 7, 12, 8, 9, 11, 7, 7, 12, 7, 6, 15, 13, 11,
38042                   9, 7, 15, 11, 8, 6, 6, 14, 12, 13, 5, 14, 13, 13, 7, 5,
38043                   15, 5, 8, 11, 14, 14, 6, 14, 6, 9, 12, 9, 12, 5, 15, 8,
38044                   8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11
38045                 ];
38046
38047               /* privileged (public) methods */
38048               this.hex = function(s) {
38049                 return rstr2hex(rstr(s));
38050               };
38051               this.b64 = function(s) {
38052                 return rstr2b64(rstr(s), b64pad);
38053               };
38054               this.any = function(s, e) {
38055                 return rstr2any(rstr(s), e);
38056               };
38057               this.raw = function(s) {
38058                 return rstr(s);
38059               };
38060               this.hex_hmac = function(k, d) {
38061                 return rstr2hex(rstr_hmac(k, d));
38062               };
38063               this.b64_hmac = function(k, d) {
38064                 return rstr2b64(rstr_hmac(k, d), b64pad);
38065               };
38066               this.any_hmac = function(k, d, e) {
38067                 return rstr2any(rstr_hmac(k, d), e);
38068               };
38069               /**
38070                * Perform a simple self-test to see if the VM is working
38071                * @return {String} Hexadecimal hash sample
38072                * @public
38073                */
38074               this.vm_test = function() {
38075                 return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
38076               };
38077               /**
38078                * @description Enable/disable uppercase hexadecimal returned string
38079                * @param {boolean}
38080                * @return {Object} this
38081                * @public
38082                */
38083               this.setUpperCase = function(a) {
38084                 if (typeof a === 'boolean') {
38085                   hexcase = a;
38086                 }
38087                 return this;
38088               };
38089               /**
38090                * @description Defines a base64 pad string
38091                * @param {string} Pad
38092                * @return {Object} this
38093                * @public
38094                */
38095               this.setPad = function(a) {
38096                 if (typeof a !== 'undefined') {
38097                   b64pad = a;
38098                 }
38099                 return this;
38100               };
38101               /**
38102                * @description Defines a base64 pad string
38103                * @param {boolean}
38104                * @return {Object} this
38105                * @public
38106                */
38107               this.setUTF8 = function(a) {
38108                 if (typeof a === 'boolean') {
38109                   utf8 = a;
38110                 }
38111                 return this;
38112               };
38113
38114               /* private methods */
38115
38116               /**
38117                * Calculate the rmd160 of a raw string
38118                */
38119
38120               function rstr(s) {
38121                 s = (utf8) ? utf8Encode(s) : s;
38122                 return binl2rstr(binl(rstr2binl(s), s.length * 8));
38123               }
38124
38125               /**
38126                * Calculate the HMAC-rmd160 of a key and some data (raw strings)
38127                */
38128
38129               function rstr_hmac(key, data) {
38130                 key = (utf8) ? utf8Encode(key) : key;
38131                 data = (utf8) ? utf8Encode(data) : data;
38132                 var i, hash,
38133                   bkey = rstr2binl(key),
38134                   ipad = Array(16),
38135                   opad = Array(16);
38136
38137                 if (bkey.length > 16) {
38138                   bkey = binl(bkey, key.length * 8);
38139                 }
38140
38141                 for (i = 0; i < 16; i += 1) {
38142                   ipad[i] = bkey[i] ^ 0x36363636;
38143                   opad[i] = bkey[i] ^ 0x5C5C5C5C;
38144                 }
38145                 hash = binl(ipad.concat(rstr2binl(data)), 512 + data.length * 8);
38146                 return binl2rstr(binl(opad.concat(hash), 512 + 160));
38147               }
38148
38149               /**
38150                * Convert an array of little-endian words to a string
38151                */
38152
38153               function binl2rstr(input) {
38154                 var i, output = '',
38155                   l = input.length * 32;
38156                 for (i = 0; i < l; i += 8) {
38157                   output += String.fromCharCode((input[i >> 5] >>> (i % 32)) & 0xFF);
38158                 }
38159                 return output;
38160               }
38161
38162               /**
38163                * Calculate the RIPE-MD160 of an array of little-endian words, and a bit length.
38164                */
38165
38166               function binl(x, len) {
38167                 var T, j, i, l,
38168                   h0 = 0x67452301,
38169                   h1 = 0xefcdab89,
38170                   h2 = 0x98badcfe,
38171                   h3 = 0x10325476,
38172                   h4 = 0xc3d2e1f0,
38173                   A1, B1, C1, D1, E1,
38174                   A2, B2, C2, D2, E2;
38175
38176                 /* append padding */
38177                 x[len >> 5] |= 0x80 << (len % 32);
38178                 x[(((len + 64) >>> 9) << 4) + 14] = len;
38179                 l = x.length;
38180
38181                 for (i = 0; i < l; i += 16) {
38182                   A1 = A2 = h0;
38183                   B1 = B2 = h1;
38184                   C1 = C2 = h2;
38185                   D1 = D2 = h3;
38186                   E1 = E2 = h4;
38187                   for (j = 0; j <= 79; j += 1) {
38188                     T = safe_add(A1, rmd160_f(j, B1, C1, D1));
38189                     T = safe_add(T, x[i + rmd160_r1[j]]);
38190                     T = safe_add(T, rmd160_K1(j));
38191                     T = safe_add(bit_rol(T, rmd160_s1[j]), E1);
38192                     A1 = E1;
38193                     E1 = D1;
38194                     D1 = bit_rol(C1, 10);
38195                     C1 = B1;
38196                     B1 = T;
38197                     T = safe_add(A2, rmd160_f(79 - j, B2, C2, D2));
38198                     T = safe_add(T, x[i + rmd160_r2[j]]);
38199                     T = safe_add(T, rmd160_K2(j));
38200                     T = safe_add(bit_rol(T, rmd160_s2[j]), E2);
38201                     A2 = E2;
38202                     E2 = D2;
38203                     D2 = bit_rol(C2, 10);
38204                     C2 = B2;
38205                     B2 = T;
38206                   }
38207
38208                   T = safe_add(h1, safe_add(C1, D2));
38209                   h1 = safe_add(h2, safe_add(D1, E2));
38210                   h2 = safe_add(h3, safe_add(E1, A2));
38211                   h3 = safe_add(h4, safe_add(A1, B2));
38212                   h4 = safe_add(h0, safe_add(B1, C2));
38213                   h0 = T;
38214                 }
38215                 return [h0, h1, h2, h3, h4];
38216               }
38217
38218               // specific algorithm methods
38219
38220               function rmd160_f(j, x, y, z) {
38221                 return (0 <= j && j <= 15) ? (x ^ y ^ z) :
38222                   (16 <= j && j <= 31) ? (x & y) | (~x & z) :
38223                   (32 <= j && j <= 47) ? (x | ~y) ^ z :
38224                   (48 <= j && j <= 63) ? (x & z) | (y & ~z) :
38225                   (64 <= j && j <= 79) ? x ^ (y | ~z) :
38226                   'rmd160_f: j out of range';
38227               }
38228
38229               function rmd160_K1(j) {
38230                 return (0 <= j && j <= 15) ? 0x00000000 :
38231                   (16 <= j && j <= 31) ? 0x5a827999 :
38232                   (32 <= j && j <= 47) ? 0x6ed9eba1 :
38233                   (48 <= j && j <= 63) ? 0x8f1bbcdc :
38234                   (64 <= j && j <= 79) ? 0xa953fd4e :
38235                   'rmd160_K1: j out of range';
38236               }
38237
38238               function rmd160_K2(j) {
38239                 return (0 <= j && j <= 15) ? 0x50a28be6 :
38240                   (16 <= j && j <= 31) ? 0x5c4dd124 :
38241                   (32 <= j && j <= 47) ? 0x6d703ef3 :
38242                   (48 <= j && j <= 63) ? 0x7a6d76e9 :
38243                   (64 <= j && j <= 79) ? 0x00000000 :
38244                   'rmd160_K2: j out of range';
38245               }
38246             }
38247           };
38248
38249           // exposes Hashes
38250           (function(window, undefined$1) {
38251             var freeExports = false;
38252             {
38253               freeExports = exports;
38254               if (exports && typeof commonjsGlobal === 'object' && commonjsGlobal && commonjsGlobal === commonjsGlobal.global) {
38255                 window = commonjsGlobal;
38256               }
38257             }
38258
38259             if (typeof undefined$1 === 'function' && typeof undefined$1.amd === 'object' && undefined$1.amd) {
38260               // define as an anonymous module, so, through path mapping, it can be aliased
38261               undefined$1(function() {
38262                 return Hashes;
38263               });
38264             } else if (freeExports) {
38265               // in Node.js or RingoJS v0.8.0+
38266               if ( module && module.exports === freeExports) {
38267                 module.exports = Hashes;
38268               }
38269               // in Narwhal or RingoJS v0.7.0-
38270               else {
38271                 freeExports.Hashes = Hashes;
38272               }
38273             } else {
38274               // in a browser or Rhino
38275               window.Hashes = Hashes;
38276             }
38277           }(this));
38278         }()); // IIFE
38279         });
38280
38281         var immutable = extend$2;
38282
38283         var hasOwnProperty$3 = Object.prototype.hasOwnProperty;
38284
38285         function extend$2() {
38286             var arguments$1 = arguments;
38287
38288             var target = {};
38289
38290             for (var i = 0; i < arguments.length; i++) {
38291                 var source = arguments$1[i];
38292
38293                 for (var key in source) {
38294                     if (hasOwnProperty$3.call(source, key)) {
38295                         target[key] = source[key];
38296                     }
38297                 }
38298             }
38299
38300             return target
38301         }
38302
38303         var sha1 = new hashes.SHA1();
38304
38305         var ohauth = {};
38306
38307         ohauth.qsString = function(obj) {
38308             return Object.keys(obj).sort().map(function(key) {
38309                 return ohauth.percentEncode(key) + '=' +
38310                     ohauth.percentEncode(obj[key]);
38311             }).join('&');
38312         };
38313
38314         ohauth.stringQs = function(str) {
38315             return str.split('&').filter(function (pair) {
38316                 return pair !== '';
38317             }).reduce(function(obj, pair){
38318                 var parts = pair.split('=');
38319                 obj[decodeURIComponent(parts[0])] = (null === parts[1]) ?
38320                     '' : decodeURIComponent(parts[1]);
38321                 return obj;
38322             }, {});
38323         };
38324
38325         ohauth.rawxhr = function(method, url, data, headers, callback) {
38326             var xhr = new XMLHttpRequest(),
38327                 twoHundred = /^20\d$/;
38328             xhr.onreadystatechange = function() {
38329                 if (4 === xhr.readyState && 0 !== xhr.status) {
38330                     if (twoHundred.test(xhr.status)) { callback(null, xhr); }
38331                     else { return callback(xhr, null); }
38332                 }
38333             };
38334             xhr.onerror = function(e) { return callback(e, null); };
38335             xhr.open(method, url, true);
38336             for (var h in headers) { xhr.setRequestHeader(h, headers[h]); }
38337             xhr.send(data);
38338             return xhr;
38339         };
38340
38341         ohauth.xhr = function(method, url, auth, data, options, callback) {
38342             var headers = (options && options.header) || {
38343                 'Content-Type': 'application/x-www-form-urlencoded'
38344             };
38345             headers.Authorization = 'OAuth ' + ohauth.authHeader(auth);
38346             return ohauth.rawxhr(method, url, data, headers, callback);
38347         };
38348
38349         ohauth.nonce = function() {
38350             for (var o = ''; o.length < 6;) {
38351                 o += '0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz'[Math.floor(Math.random() * 61)];
38352             }
38353             return o;
38354         };
38355
38356         ohauth.authHeader = function(obj) {
38357             return Object.keys(obj).sort().map(function(key) {
38358                 return encodeURIComponent(key) + '="' + encodeURIComponent(obj[key]) + '"';
38359             }).join(', ');
38360         };
38361
38362         ohauth.timestamp = function() { return ~~((+new Date()) / 1000); };
38363
38364         ohauth.percentEncode = function(s) {
38365             return encodeURIComponent(s)
38366                 .replace(/\!/g, '%21').replace(/\'/g, '%27')
38367                 .replace(/\*/g, '%2A').replace(/\(/g, '%28').replace(/\)/g, '%29');
38368         };
38369
38370         ohauth.baseString = function(method, url, params) {
38371             if (params.oauth_signature) { delete params.oauth_signature; }
38372             return [
38373                 method,
38374                 ohauth.percentEncode(url),
38375                 ohauth.percentEncode(ohauth.qsString(params))].join('&');
38376         };
38377
38378         ohauth.signature = function(oauth_secret, token_secret, baseString) {
38379             return sha1.b64_hmac(
38380                 ohauth.percentEncode(oauth_secret) + '&' +
38381                 ohauth.percentEncode(token_secret),
38382                 baseString);
38383         };
38384
38385         /**
38386          * Takes an options object for configuration (consumer_key,
38387          * consumer_secret, version, signature_method, token, token_secret)
38388          * and returns a function that generates the Authorization header
38389          * for given data.
38390          *
38391          * The returned function takes these parameters:
38392          * - method: GET/POST/...
38393          * - uri: full URI with protocol, port, path and query string
38394          * - extra_params: any extra parameters (that are passed in the POST data),
38395          *   can be an object or a from-urlencoded string.
38396          *
38397          * Returned function returns full OAuth header with "OAuth" string in it.
38398          */
38399
38400         ohauth.headerGenerator = function(options) {
38401             options = options || {};
38402             var consumer_key = options.consumer_key || '',
38403                 consumer_secret = options.consumer_secret || '',
38404                 signature_method = options.signature_method || 'HMAC-SHA1',
38405                 version = options.version || '1.0',
38406                 token = options.token || '',
38407                 token_secret = options.token_secret || '';
38408
38409             return function(method, uri, extra_params) {
38410                 method = method.toUpperCase();
38411                 if (typeof extra_params === 'string' && extra_params.length > 0) {
38412                     extra_params = ohauth.stringQs(extra_params);
38413                 }
38414
38415                 var uri_parts = uri.split('?', 2),
38416                 base_uri = uri_parts[0];
38417
38418                 var query_params = uri_parts.length === 2 ?
38419                     ohauth.stringQs(uri_parts[1]) : {};
38420
38421                 var oauth_params = {
38422                     oauth_consumer_key: consumer_key,
38423                     oauth_signature_method: signature_method,
38424                     oauth_version: version,
38425                     oauth_timestamp: ohauth.timestamp(),
38426                     oauth_nonce: ohauth.nonce()
38427                 };
38428
38429                 if (token) { oauth_params.oauth_token = token; }
38430
38431                 var all_params = immutable({}, oauth_params, query_params, extra_params),
38432                     base_str = ohauth.baseString(method, base_uri, all_params);
38433
38434                 oauth_params.oauth_signature = ohauth.signature(consumer_secret, token_secret, base_str);
38435
38436                 return 'OAuth ' + ohauth.authHeader(oauth_params);
38437             };
38438         };
38439
38440         var ohauth_1 = ohauth;
38441
38442         var resolveUrl$1 = createCommonjsModule(function (module, exports) {
38443         // Copyright 2014 Simon Lydell
38444         // X11 (“MIT”) Licensed. (See LICENSE.)
38445
38446         void (function(root, factory) {
38447           {
38448             module.exports = factory();
38449           }
38450         }(commonjsGlobal, function() {
38451
38452           function resolveUrl(/* ...urls */) {
38453             var arguments$1 = arguments;
38454
38455             var numUrls = arguments.length;
38456
38457             if (numUrls === 0) {
38458               throw new Error("resolveUrl requires at least one argument; got none.")
38459             }
38460
38461             var base = document.createElement("base");
38462             base.href = arguments[0];
38463
38464             if (numUrls === 1) {
38465               return base.href
38466             }
38467
38468             var head = document.getElementsByTagName("head")[0];
38469             head.insertBefore(base, head.firstChild);
38470
38471             var a = document.createElement("a");
38472             var resolved;
38473
38474             for (var index = 1; index < numUrls; index++) {
38475               a.href = arguments$1[index];
38476               resolved = a.href;
38477               base.href = resolved;
38478             }
38479
38480             head.removeChild(base);
38481
38482             return resolved
38483           }
38484
38485           return resolveUrl
38486
38487         }));
38488         });
38489
38490         var assign$1 = make_assign();
38491         var create$8 = make_create();
38492         var trim = make_trim();
38493         var Global = (typeof window !== 'undefined' ? window : commonjsGlobal);
38494
38495         var util = {
38496                 assign: assign$1,
38497                 create: create$8,
38498                 trim: trim,
38499                 bind: bind$3,
38500                 slice: slice$5,
38501                 each: each,
38502                 map: map$5,
38503                 pluck: pluck,
38504                 isList: isList,
38505                 isFunction: isFunction$2,
38506                 isObject: isObject$2,
38507                 Global: Global
38508         };
38509
38510         function make_assign() {
38511                 if (Object.assign) {
38512                         return Object.assign
38513                 } else {
38514                         return function shimAssign(obj, props1, props2, etc) {
38515                                 var arguments$1 = arguments;
38516
38517                                 for (var i = 1; i < arguments.length; i++) {
38518                                         each(Object(arguments$1[i]), function(val, key) {
38519                                                 obj[key] = val;
38520                                         });
38521                                 }                       
38522                                 return obj
38523                         }
38524                 }
38525         }
38526
38527         function make_create() {
38528                 if (Object.create) {
38529                         return function create(obj, assignProps1, assignProps2, etc) {
38530                                 var assignArgsList = slice$5(arguments, 1);
38531                                 return assign$1.apply(this, [Object.create(obj)].concat(assignArgsList))
38532                         }
38533                 } else {
38534                         function F() {} // eslint-disable-line no-inner-declarations
38535                         return function create(obj, assignProps1, assignProps2, etc) {
38536                                 var assignArgsList = slice$5(arguments, 1);
38537                                 F.prototype = obj;
38538                                 return assign$1.apply(this, [new F()].concat(assignArgsList))
38539                         }
38540                 }
38541         }
38542
38543         function make_trim() {
38544                 if (String.prototype.trim) {
38545                         return function trim(str) {
38546                                 return String.prototype.trim.call(str)
38547                         }
38548                 } else {
38549                         return function trim(str) {
38550                                 return str.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '')
38551                         }
38552                 }
38553         }
38554
38555         function bind$3(obj, fn) {
38556                 return function() {
38557                         return fn.apply(obj, Array.prototype.slice.call(arguments, 0))
38558                 }
38559         }
38560
38561         function slice$5(arr, index) {
38562                 return Array.prototype.slice.call(arr, index || 0)
38563         }
38564
38565         function each(obj, fn) {
38566                 pluck(obj, function(val, key) {
38567                         fn(val, key);
38568                         return false
38569                 });
38570         }
38571
38572         function map$5(obj, fn) {
38573                 var res = (isList(obj) ? [] : {});
38574                 pluck(obj, function(v, k) {
38575                         res[k] = fn(v, k);
38576                         return false
38577                 });
38578                 return res
38579         }
38580
38581         function pluck(obj, fn) {
38582                 if (isList(obj)) {
38583                         for (var i=0; i<obj.length; i++) {
38584                                 if (fn(obj[i], i)) {
38585                                         return obj[i]
38586                                 }
38587                         }
38588                 } else {
38589                         for (var key in obj) {
38590                                 if (obj.hasOwnProperty(key)) {
38591                                         if (fn(obj[key], key)) {
38592                                                 return obj[key]
38593                                         }
38594                                 }
38595                         }
38596                 }
38597         }
38598
38599         function isList(val) {
38600                 return (val != null && typeof val != 'function' && typeof val.length == 'number')
38601         }
38602
38603         function isFunction$2(val) {
38604                 return val && {}.toString.call(val) === '[object Function]'
38605         }
38606
38607         function isObject$2(val) {
38608                 return val && {}.toString.call(val) === '[object Object]'
38609         }
38610
38611         var slice$6 = util.slice;
38612         var pluck$1 = util.pluck;
38613         var each$1 = util.each;
38614         var bind$4 = util.bind;
38615         var create$9 = util.create;
38616         var isList$1 = util.isList;
38617         var isFunction$3 = util.isFunction;
38618         var isObject$3 = util.isObject;
38619
38620         var storeEngine = {
38621                 createStore: createStore
38622         };
38623
38624         var storeAPI = {
38625                 version: '2.0.12',
38626                 enabled: false,
38627                 
38628                 // get returns the value of the given key. If that value
38629                 // is undefined, it returns optionalDefaultValue instead.
38630                 get: function(key, optionalDefaultValue) {
38631                         var data = this.storage.read(this._namespacePrefix + key);
38632                         return this._deserialize(data, optionalDefaultValue)
38633                 },
38634
38635                 // set will store the given value at key and returns value.
38636                 // Calling set with value === undefined is equivalent to calling remove.
38637                 set: function(key, value) {
38638                         if (value === undefined) {
38639                                 return this.remove(key)
38640                         }
38641                         this.storage.write(this._namespacePrefix + key, this._serialize(value));
38642                         return value
38643                 },
38644
38645                 // remove deletes the key and value stored at the given key.
38646                 remove: function(key) {
38647                         this.storage.remove(this._namespacePrefix + key);
38648                 },
38649
38650                 // each will call the given callback once for each key-value pair
38651                 // in this store.
38652                 each: function(callback) {
38653                         var self = this;
38654                         this.storage.each(function(val, namespacedKey) {
38655                                 callback.call(self, self._deserialize(val), (namespacedKey || '').replace(self._namespaceRegexp, ''));
38656                         });
38657                 },
38658
38659                 // clearAll will remove all the stored key-value pairs in this store.
38660                 clearAll: function() {
38661                         this.storage.clearAll();
38662                 },
38663
38664                 // additional functionality that can't live in plugins
38665                 // ---------------------------------------------------
38666
38667                 // hasNamespace returns true if this store instance has the given namespace.
38668                 hasNamespace: function(namespace) {
38669                         return (this._namespacePrefix == '__storejs_'+namespace+'_')
38670                 },
38671
38672                 // createStore creates a store.js instance with the first
38673                 // functioning storage in the list of storage candidates,
38674                 // and applies the the given mixins to the instance.
38675                 createStore: function() {
38676                         return createStore.apply(this, arguments)
38677                 },
38678                 
38679                 addPlugin: function(plugin) {
38680                         this._addPlugin(plugin);
38681                 },
38682                 
38683                 namespace: function(namespace) {
38684                         return createStore(this.storage, this.plugins, namespace)
38685                 }
38686         };
38687
38688         function _warn() {
38689                 var _console = (typeof console == 'undefined' ? null : console);
38690                 if (!_console) { return }
38691                 var fn = (_console.warn ? _console.warn : _console.log);
38692                 fn.apply(_console, arguments);
38693         }
38694
38695         function createStore(storages, plugins, namespace) {
38696                 if (!namespace) {
38697                         namespace = '';
38698                 }
38699                 if (storages && !isList$1(storages)) {
38700                         storages = [storages];
38701                 }
38702                 if (plugins && !isList$1(plugins)) {
38703                         plugins = [plugins];
38704                 }
38705
38706                 var namespacePrefix = (namespace ? '__storejs_'+namespace+'_' : '');
38707                 var namespaceRegexp = (namespace ? new RegExp('^'+namespacePrefix) : null);
38708                 var legalNamespaces = /^[a-zA-Z0-9_\-]*$/; // alpha-numeric + underscore and dash
38709                 if (!legalNamespaces.test(namespace)) {
38710                         throw new Error('store.js namespaces can only have alphanumerics + underscores and dashes')
38711                 }
38712                 
38713                 var _privateStoreProps = {
38714                         _namespacePrefix: namespacePrefix,
38715                         _namespaceRegexp: namespaceRegexp,
38716
38717                         _testStorage: function(storage) {
38718                                 try {
38719                                         var testStr = '__storejs__test__';
38720                                         storage.write(testStr, testStr);
38721                                         var ok = (storage.read(testStr) === testStr);
38722                                         storage.remove(testStr);
38723                                         return ok
38724                                 } catch(e) {
38725                                         return false
38726                                 }
38727                         },
38728
38729                         _assignPluginFnProp: function(pluginFnProp, propName) {
38730                                 var oldFn = this[propName];
38731                                 this[propName] = function pluginFn() {
38732                                         var args = slice$6(arguments, 0);
38733                                         var self = this;
38734
38735                                         // super_fn calls the old function which was overwritten by
38736                                         // this mixin.
38737                                         function super_fn() {
38738                                                 if (!oldFn) { return }
38739                                                 each$1(arguments, function(arg, i) {
38740                                                         args[i] = arg;
38741                                                 });
38742                                                 return oldFn.apply(self, args)
38743                                         }
38744
38745                                         // Give mixing function access to super_fn by prefixing all mixin function
38746                                         // arguments with super_fn.
38747                                         var newFnArgs = [super_fn].concat(args);
38748
38749                                         return pluginFnProp.apply(self, newFnArgs)
38750                                 };
38751                         },
38752
38753                         _serialize: function(obj) {
38754                                 return JSON.stringify(obj)
38755                         },
38756
38757                         _deserialize: function(strVal, defaultVal) {
38758                                 if (!strVal) { return defaultVal }
38759                                 // It is possible that a raw string value has been previously stored
38760                                 // in a storage without using store.js, meaning it will be a raw
38761                                 // string value instead of a JSON serialized string. By defaulting
38762                                 // to the raw string value in case of a JSON parse error, we allow
38763                                 // for past stored values to be forwards-compatible with store.js
38764                                 var val = '';
38765                                 try { val = JSON.parse(strVal); }
38766                                 catch(e) { val = strVal; }
38767
38768                                 return (val !== undefined ? val : defaultVal)
38769                         },
38770                         
38771                         _addStorage: function(storage) {
38772                                 if (this.enabled) { return }
38773                                 if (this._testStorage(storage)) {
38774                                         this.storage = storage;
38775                                         this.enabled = true;
38776                                 }
38777                         },
38778
38779                         _addPlugin: function(plugin) {
38780                                 var self = this;
38781
38782                                 // If the plugin is an array, then add all plugins in the array.
38783                                 // This allows for a plugin to depend on other plugins.
38784                                 if (isList$1(plugin)) {
38785                                         each$1(plugin, function(plugin) {
38786                                                 self._addPlugin(plugin);
38787                                         });
38788                                         return
38789                                 }
38790
38791                                 // Keep track of all plugins we've seen so far, so that we
38792                                 // don't add any of them twice.
38793                                 var seenPlugin = pluck$1(this.plugins, function(seenPlugin) {
38794                                         return (plugin === seenPlugin)
38795                                 });
38796                                 if (seenPlugin) {
38797                                         return
38798                                 }
38799                                 this.plugins.push(plugin);
38800
38801                                 // Check that the plugin is properly formed
38802                                 if (!isFunction$3(plugin)) {
38803                                         throw new Error('Plugins must be function values that return objects')
38804                                 }
38805
38806                                 var pluginProperties = plugin.call(this);
38807                                 if (!isObject$3(pluginProperties)) {
38808                                         throw new Error('Plugins must return an object of function properties')
38809                                 }
38810
38811                                 // Add the plugin function properties to this store instance.
38812                                 each$1(pluginProperties, function(pluginFnProp, propName) {
38813                                         if (!isFunction$3(pluginFnProp)) {
38814                                                 throw new Error('Bad plugin property: '+propName+' from plugin '+plugin.name+'. Plugins should only return functions.')
38815                                         }
38816                                         self._assignPluginFnProp(pluginFnProp, propName);
38817                                 });
38818                         },
38819                         
38820                         // Put deprecated properties in the private API, so as to not expose it to accidential
38821                         // discovery through inspection of the store object.
38822                         
38823                         // Deprecated: addStorage
38824                         addStorage: function(storage) {
38825                                 _warn('store.addStorage(storage) is deprecated. Use createStore([storages])');
38826                                 this._addStorage(storage);
38827                         }
38828                 };
38829
38830                 var store = create$9(_privateStoreProps, storeAPI, {
38831                         plugins: []
38832                 });
38833                 store.raw = {};
38834                 each$1(store, function(prop, propName) {
38835                         if (isFunction$3(prop)) {
38836                                 store.raw[propName] = bind$4(store, prop);                      
38837                         }
38838                 });
38839                 each$1(storages, function(storage) {
38840                         store._addStorage(storage);
38841                 });
38842                 each$1(plugins, function(plugin) {
38843                         store._addPlugin(plugin);
38844                 });
38845                 return store
38846         }
38847
38848         var Global$1 = util.Global;
38849
38850         var localStorage_1 = {
38851                 name: 'localStorage',
38852                 read: read,
38853                 write: write,
38854                 each: each$2,
38855                 remove: remove$2,
38856                 clearAll: clearAll,
38857         };
38858
38859         function localStorage$1() {
38860                 return Global$1.localStorage
38861         }
38862
38863         function read(key) {
38864                 return localStorage$1().getItem(key)
38865         }
38866
38867         function write(key, data) {
38868                 return localStorage$1().setItem(key, data)
38869         }
38870
38871         function each$2(fn) {
38872                 for (var i = localStorage$1().length - 1; i >= 0; i--) {
38873                         var key = localStorage$1().key(i);
38874                         fn(read(key), key);
38875                 }
38876         }
38877
38878         function remove$2(key) {
38879                 return localStorage$1().removeItem(key)
38880         }
38881
38882         function clearAll() {
38883                 return localStorage$1().clear()
38884         }
38885
38886         // oldFF-globalStorage provides storage for Firefox
38887         // versions 6 and 7, where no localStorage, etc
38888         // is available.
38889
38890
38891         var Global$2 = util.Global;
38892
38893         var oldFFGlobalStorage = {
38894                 name: 'oldFF-globalStorage',
38895                 read: read$1,
38896                 write: write$1,
38897                 each: each$3,
38898                 remove: remove$3,
38899                 clearAll: clearAll$1,
38900         };
38901
38902         var globalStorage = Global$2.globalStorage;
38903
38904         function read$1(key) {
38905                 return globalStorage[key]
38906         }
38907
38908         function write$1(key, data) {
38909                 globalStorage[key] = data;
38910         }
38911
38912         function each$3(fn) {
38913                 for (var i = globalStorage.length - 1; i >= 0; i--) {
38914                         var key = globalStorage.key(i);
38915                         fn(globalStorage[key], key);
38916                 }
38917         }
38918
38919         function remove$3(key) {
38920                 return globalStorage.removeItem(key)
38921         }
38922
38923         function clearAll$1() {
38924                 each$3(function(key, _) {
38925                         delete globalStorage[key];
38926                 });
38927         }
38928
38929         // oldIE-userDataStorage provides storage for Internet Explorer
38930         // versions 6 and 7, where no localStorage, sessionStorage, etc
38931         // is available.
38932
38933
38934         var Global$3 = util.Global;
38935
38936         var oldIEUserDataStorage = {
38937                 name: 'oldIE-userDataStorage',
38938                 write: write$2,
38939                 read: read$2,
38940                 each: each$4,
38941                 remove: remove$4,
38942                 clearAll: clearAll$2,
38943         };
38944
38945         var storageName = 'storejs';
38946         var doc = Global$3.document;
38947         var _withStorageEl = _makeIEStorageElFunction();
38948         var disable = (Global$3.navigator ? Global$3.navigator.userAgent : '').match(/ (MSIE 8|MSIE 9|MSIE 10)\./); // MSIE 9.x, MSIE 10.x
38949
38950         function write$2(unfixedKey, data) {
38951                 if (disable) { return }
38952                 var fixedKey = fixKey(unfixedKey);
38953                 _withStorageEl(function(storageEl) {
38954                         storageEl.setAttribute(fixedKey, data);
38955                         storageEl.save(storageName);
38956                 });
38957         }
38958
38959         function read$2(unfixedKey) {
38960                 if (disable) { return }
38961                 var fixedKey = fixKey(unfixedKey);
38962                 var res = null;
38963                 _withStorageEl(function(storageEl) {
38964                         res = storageEl.getAttribute(fixedKey);
38965                 });
38966                 return res
38967         }
38968
38969         function each$4(callback) {
38970                 _withStorageEl(function(storageEl) {
38971                         var attributes = storageEl.XMLDocument.documentElement.attributes;
38972                         for (var i=attributes.length-1; i>=0; i--) {
38973                                 var attr = attributes[i];
38974                                 callback(storageEl.getAttribute(attr.name), attr.name);
38975                         }
38976                 });
38977         }
38978
38979         function remove$4(unfixedKey) {
38980                 var fixedKey = fixKey(unfixedKey);
38981                 _withStorageEl(function(storageEl) {
38982                         storageEl.removeAttribute(fixedKey);
38983                         storageEl.save(storageName);
38984                 });
38985         }
38986
38987         function clearAll$2() {
38988                 _withStorageEl(function(storageEl) {
38989                         var attributes = storageEl.XMLDocument.documentElement.attributes;
38990                         storageEl.load(storageName);
38991                         for (var i=attributes.length-1; i>=0; i--) {
38992                                 storageEl.removeAttribute(attributes[i].name);
38993                         }
38994                         storageEl.save(storageName);
38995                 });
38996         }
38997
38998         // Helpers
38999         //////////
39000
39001         // In IE7, keys cannot start with a digit or contain certain chars.
39002         // See https://github.com/marcuswestin/store.js/issues/40
39003         // See https://github.com/marcuswestin/store.js/issues/83
39004         var forbiddenCharsRegex = new RegExp("[!\"#$%&'()*+,/\\\\:;<=>?@[\\]^`{|}~]", "g");
39005         function fixKey(key) {
39006                 return key.replace(/^\d/, '___$&').replace(forbiddenCharsRegex, '___')
39007         }
39008
39009         function _makeIEStorageElFunction() {
39010                 if (!doc || !doc.documentElement || !doc.documentElement.addBehavior) {
39011                         return null
39012                 }
39013                 var scriptTag = 'script',
39014                         storageOwner,
39015                         storageContainer,
39016                         storageEl;
39017
39018                 // Since #userData storage applies only to specific paths, we need to
39019                 // somehow link our data to a specific path.  We choose /favicon.ico
39020                 // as a pretty safe option, since all browsers already make a request to
39021                 // this URL anyway and being a 404 will not hurt us here.  We wrap an
39022                 // iframe pointing to the favicon in an ActiveXObject(htmlfile) object
39023                 // (see: http://msdn.microsoft.com/en-us/library/aa752574(v=VS.85).aspx)
39024                 // since the iframe access rules appear to allow direct access and
39025                 // manipulation of the document element, even for a 404 page.  This
39026                 // document can be used instead of the current document (which would
39027                 // have been limited to the current path) to perform #userData storage.
39028                 try {
39029                         /* global ActiveXObject */
39030                         storageContainer = new ActiveXObject('htmlfile');
39031                         storageContainer.open();
39032                         storageContainer.write('<'+scriptTag+'>document.w=window</'+scriptTag+'><iframe src="/favicon.ico"></iframe>');
39033                         storageContainer.close();
39034                         storageOwner = storageContainer.w.frames[0].document;
39035                         storageEl = storageOwner.createElement('div');
39036                 } catch(e) {
39037                         // somehow ActiveXObject instantiation failed (perhaps some special
39038                         // security settings or otherwse), fall back to per-path storage
39039                         storageEl = doc.createElement('div');
39040                         storageOwner = doc.body;
39041                 }
39042
39043                 return function(storeFunction) {
39044                         var args = [].slice.call(arguments, 0);
39045                         args.unshift(storageEl);
39046                         // See http://msdn.microsoft.com/en-us/library/ms531081(v=VS.85).aspx
39047                         // and http://msdn.microsoft.com/en-us/library/ms531424(v=VS.85).aspx
39048                         storageOwner.appendChild(storageEl);
39049                         storageEl.addBehavior('#default#userData');
39050                         storageEl.load(storageName);
39051                         storeFunction.apply(this, args);
39052                         storageOwner.removeChild(storageEl);
39053                         return
39054                 }
39055         }
39056
39057         // cookieStorage is useful Safari private browser mode, where localStorage
39058         // doesn't work but cookies do. This implementation is adopted from
39059         // https://developer.mozilla.org/en-US/docs/Web/API/Storage/LocalStorage
39060
39061
39062         var Global$4 = util.Global;
39063         var trim$1 = util.trim;
39064
39065         var cookieStorage = {
39066                 name: 'cookieStorage',
39067                 read: read$3,
39068                 write: write$3,
39069                 each: each$5,
39070                 remove: remove$5,
39071                 clearAll: clearAll$3,
39072         };
39073
39074         var doc$1 = Global$4.document;
39075
39076         function read$3(key) {
39077                 if (!key || !_has(key)) { return null }
39078                 var regexpStr = "(?:^|.*;\\s*)" +
39079                         escape(key).replace(/[\-\.\+\*]/g, "\\$&") +
39080                         "\\s*\\=\\s*((?:[^;](?!;))*[^;]?).*";
39081                 return unescape(doc$1.cookie.replace(new RegExp(regexpStr), "$1"))
39082         }
39083
39084         function each$5(callback) {
39085                 var cookies = doc$1.cookie.split(/; ?/g);
39086                 for (var i = cookies.length - 1; i >= 0; i--) {
39087                         if (!trim$1(cookies[i])) {
39088                                 continue
39089                         }
39090                         var kvp = cookies[i].split('=');
39091                         var key = unescape(kvp[0]);
39092                         var val = unescape(kvp[1]);
39093                         callback(val, key);
39094                 }
39095         }
39096
39097         function write$3(key, data) {
39098                 if(!key) { return }
39099                 doc$1.cookie = escape(key) + "=" + escape(data) + "; expires=Tue, 19 Jan 2038 03:14:07 GMT; path=/";
39100         }
39101
39102         function remove$5(key) {
39103                 if (!key || !_has(key)) {
39104                         return
39105                 }
39106                 doc$1.cookie = escape(key) + "=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/";
39107         }
39108
39109         function clearAll$3() {
39110                 each$5(function(_, key) {
39111                         remove$5(key);
39112                 });
39113         }
39114
39115         function _has(key) {
39116                 return (new RegExp("(?:^|;\\s*)" + escape(key).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=")).test(doc$1.cookie)
39117         }
39118
39119         var Global$5 = util.Global;
39120
39121         var sessionStorage_1 = {
39122                 name: 'sessionStorage',
39123                 read: read$4,
39124                 write: write$4,
39125                 each: each$6,
39126                 remove: remove$6,
39127                 clearAll: clearAll$4
39128         };
39129
39130         function sessionStorage() {
39131                 return Global$5.sessionStorage
39132         }
39133
39134         function read$4(key) {
39135                 return sessionStorage().getItem(key)
39136         }
39137
39138         function write$4(key, data) {
39139                 return sessionStorage().setItem(key, data)
39140         }
39141
39142         function each$6(fn) {
39143                 for (var i = sessionStorage().length - 1; i >= 0; i--) {
39144                         var key = sessionStorage().key(i);
39145                         fn(read$4(key), key);
39146                 }
39147         }
39148
39149         function remove$6(key) {
39150                 return sessionStorage().removeItem(key)
39151         }
39152
39153         function clearAll$4() {
39154                 return sessionStorage().clear()
39155         }
39156
39157         // memoryStorage is a useful last fallback to ensure that the store
39158         // is functions (meaning store.get(), store.set(), etc will all function).
39159         // However, stored values will not persist when the browser navigates to
39160         // a new page or reloads the current page.
39161
39162         var memoryStorage_1 = {
39163                 name: 'memoryStorage',
39164                 read: read$5,
39165                 write: write$5,
39166                 each: each$7,
39167                 remove: remove$7,
39168                 clearAll: clearAll$5,
39169         };
39170
39171         var memoryStorage = {};
39172
39173         function read$5(key) {
39174                 return memoryStorage[key]
39175         }
39176
39177         function write$5(key, data) {
39178                 memoryStorage[key] = data;
39179         }
39180
39181         function each$7(callback) {
39182                 for (var key in memoryStorage) {
39183                         if (memoryStorage.hasOwnProperty(key)) {
39184                                 callback(memoryStorage[key], key);
39185                         }
39186                 }
39187         }
39188
39189         function remove$7(key) {
39190                 delete memoryStorage[key];
39191         }
39192
39193         function clearAll$5(key) {
39194                 memoryStorage = {};
39195         }
39196
39197         var all = [
39198                 // Listed in order of usage preference
39199                 localStorage_1,
39200                 oldFFGlobalStorage,
39201                 oldIEUserDataStorage,
39202                 cookieStorage,
39203                 sessionStorage_1,
39204                 memoryStorage_1
39205         ];
39206
39207         /* eslint-disable */
39208
39209         //  json2.js
39210         //  2016-10-28
39211         //  Public Domain.
39212         //  NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
39213         //  See http://www.JSON.org/js.html
39214         //  This code should be minified before deployment.
39215         //  See http://javascript.crockford.com/jsmin.html
39216
39217         //  USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
39218         //  NOT CONTROL.
39219
39220         //  This file creates a global JSON object containing two methods: stringify
39221         //  and parse. This file provides the ES5 JSON capability to ES3 systems.
39222         //  If a project might run on IE8 or earlier, then this file should be included.
39223         //  This file does nothing on ES5 systems.
39224
39225         //      JSON.stringify(value, replacer, space)
39226         //          value       any JavaScript value, usually an object or array.
39227         //          replacer    an optional parameter that determines how object
39228         //                      values are stringified for objects. It can be a
39229         //                      function or an array of strings.
39230         //          space       an optional parameter that specifies the indentation
39231         //                      of nested structures. If it is omitted, the text will
39232         //                      be packed without extra whitespace. If it is a number,
39233         //                      it will specify the number of spaces to indent at each
39234         //                      level. If it is a string (such as "\t" or "&nbsp;"),
39235         //                      it contains the characters used to indent at each level.
39236         //          This method produces a JSON text from a JavaScript value.
39237         //          When an object value is found, if the object contains a toJSON
39238         //          method, its toJSON method will be called and the result will be
39239         //          stringified. A toJSON method does not serialize: it returns the
39240         //          value represented by the name/value pair that should be serialized,
39241         //          or undefined if nothing should be serialized. The toJSON method
39242         //          will be passed the key associated with the value, and this will be
39243         //          bound to the value.
39244
39245         //          For example, this would serialize Dates as ISO strings.
39246
39247         //              Date.prototype.toJSON = function (key) {
39248         //                  function f(n) {
39249         //                      // Format integers to have at least two digits.
39250         //                      return (n < 10)
39251         //                          ? "0" + n
39252         //                          : n;
39253         //                  }
39254         //                  return this.getUTCFullYear()   + "-" +
39255         //                       f(this.getUTCMonth() + 1) + "-" +
39256         //                       f(this.getUTCDate())      + "T" +
39257         //                       f(this.getUTCHours())     + ":" +
39258         //                       f(this.getUTCMinutes())   + ":" +
39259         //                       f(this.getUTCSeconds())   + "Z";
39260         //              };
39261
39262         //          You can provide an optional replacer method. It will be passed the
39263         //          key and value of each member, with this bound to the containing
39264         //          object. The value that is returned from your method will be
39265         //          serialized. If your method returns undefined, then the member will
39266         //          be excluded from the serialization.
39267
39268         //          If the replacer parameter is an array of strings, then it will be
39269         //          used to select the members to be serialized. It filters the results
39270         //          such that only members with keys listed in the replacer array are
39271         //          stringified.
39272
39273         //          Values that do not have JSON representations, such as undefined or
39274         //          functions, will not be serialized. Such values in objects will be
39275         //          dropped; in arrays they will be replaced with null. You can use
39276         //          a replacer function to replace those with JSON values.
39277
39278         //          JSON.stringify(undefined) returns undefined.
39279
39280         //          The optional space parameter produces a stringification of the
39281         //          value that is filled with line breaks and indentation to make it
39282         //          easier to read.
39283
39284         //          If the space parameter is a non-empty string, then that string will
39285         //          be used for indentation. If the space parameter is a number, then
39286         //          the indentation will be that many spaces.
39287
39288         //          Example:
39289
39290         //          text = JSON.stringify(["e", {pluribus: "unum"}]);
39291         //          // text is '["e",{"pluribus":"unum"}]'
39292
39293         //          text = JSON.stringify(["e", {pluribus: "unum"}], null, "\t");
39294         //          // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
39295
39296         //          text = JSON.stringify([new Date()], function (key, value) {
39297         //              return this[key] instanceof Date
39298         //                  ? "Date(" + this[key] + ")"
39299         //                  : value;
39300         //          });
39301         //          // text is '["Date(---current time---)"]'
39302
39303         //      JSON.parse(text, reviver)
39304         //          This method parses a JSON text to produce an object or array.
39305         //          It can throw a SyntaxError exception.
39306
39307         //          The optional reviver parameter is a function that can filter and
39308         //          transform the results. It receives each of the keys and values,
39309         //          and its return value is used instead of the original value.
39310         //          If it returns what it received, then the structure is not modified.
39311         //          If it returns undefined then the member is deleted.
39312
39313         //          Example:
39314
39315         //          // Parse the text. Values that look like ISO date strings will
39316         //          // be converted to Date objects.
39317
39318         //          myData = JSON.parse(text, function (key, value) {
39319         //              var a;
39320         //              if (typeof value === "string") {
39321         //                  a =
39322         //   /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
39323         //                  if (a) {
39324         //                      return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
39325         //                          +a[5], +a[6]));
39326         //                  }
39327         //              }
39328         //              return value;
39329         //          });
39330
39331         //          myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
39332         //              var d;
39333         //              if (typeof value === "string" &&
39334         //                      value.slice(0, 5) === "Date(" &&
39335         //                      value.slice(-1) === ")") {
39336         //                  d = new Date(value.slice(5, -1));
39337         //                  if (d) {
39338         //                      return d;
39339         //                  }
39340         //              }
39341         //              return value;
39342         //          });
39343
39344         //  This is a reference implementation. You are free to copy, modify, or
39345         //  redistribute.
39346
39347         /*jslint
39348             eval, for, this
39349         */
39350
39351         /*property
39352             JSON, apply, call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
39353             getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
39354             lastIndex, length, parse, prototype, push, replace, slice, stringify,
39355             test, toJSON, toString, valueOf
39356         */
39357
39358
39359         // Create a JSON object only if one does not already exist. We create the
39360         // methods in a closure to avoid creating global variables.
39361
39362         if (typeof JSON !== "object") {
39363             JSON = {};
39364         }
39365
39366         (function () {
39367
39368             var rx_one = /^[\],:{}\s]*$/;
39369             var rx_two = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g;
39370             var rx_three = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g;
39371             var rx_four = /(?:^|:|,)(?:\s*\[)+/g;
39372             var rx_escapable = /[\\"\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
39373             var rx_dangerous = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
39374
39375             function f(n) {
39376                 // Format integers to have at least two digits.
39377                 return n < 10
39378                     ? "0" + n
39379                     : n;
39380             }
39381
39382             function this_value() {
39383                 return this.valueOf();
39384             }
39385
39386             if (typeof Date.prototype.toJSON !== "function") {
39387
39388                 Date.prototype.toJSON = function () {
39389
39390                     return isFinite(this.valueOf())
39391                         ? this.getUTCFullYear() + "-" +
39392                                 f(this.getUTCMonth() + 1) + "-" +
39393                                 f(this.getUTCDate()) + "T" +
39394                                 f(this.getUTCHours()) + ":" +
39395                                 f(this.getUTCMinutes()) + ":" +
39396                                 f(this.getUTCSeconds()) + "Z"
39397                         : null;
39398                 };
39399
39400                 Boolean.prototype.toJSON = this_value;
39401                 Number.prototype.toJSON = this_value;
39402                 String.prototype.toJSON = this_value;
39403             }
39404
39405             var gap;
39406             var indent;
39407             var meta;
39408             var rep;
39409
39410
39411             function quote(string) {
39412
39413         // If the string contains no control characters, no quote characters, and no
39414         // backslash characters, then we can safely slap some quotes around it.
39415         // Otherwise we must also replace the offending characters with safe escape
39416         // sequences.
39417
39418                 rx_escapable.lastIndex = 0;
39419                 return rx_escapable.test(string)
39420                     ? "\"" + string.replace(rx_escapable, function (a) {
39421                         var c = meta[a];
39422                         return typeof c === "string"
39423                             ? c
39424                             : "\\u" + ("0000" + a.charCodeAt(0).toString(16)).slice(-4);
39425                     }) + "\""
39426                     : "\"" + string + "\"";
39427             }
39428
39429
39430             function str(key, holder) {
39431
39432         // Produce a string from holder[key].
39433
39434                 var i;          // The loop counter.
39435                 var k;          // The member key.
39436                 var v;          // The member value.
39437                 var length;
39438                 var mind = gap;
39439                 var partial;
39440                 var value = holder[key];
39441
39442         // If the value has a toJSON method, call it to obtain a replacement value.
39443
39444                 if (value && typeof value === "object" &&
39445                         typeof value.toJSON === "function") {
39446                     value = value.toJSON(key);
39447                 }
39448
39449         // If we were called with a replacer function, then call the replacer to
39450         // obtain a replacement value.
39451
39452                 if (typeof rep === "function") {
39453                     value = rep.call(holder, key, value);
39454                 }
39455
39456         // What happens next depends on the value's type.
39457
39458                 switch (typeof value) {
39459                 case "string":
39460                     return quote(value);
39461
39462                 case "number":
39463
39464         // JSON numbers must be finite. Encode non-finite numbers as null.
39465
39466                     return isFinite(value)
39467                         ? String(value)
39468                         : "null";
39469
39470                 case "boolean":
39471                 case "null":
39472
39473         // If the value is a boolean or null, convert it to a string. Note:
39474         // typeof null does not produce "null". The case is included here in
39475         // the remote chance that this gets fixed someday.
39476
39477                     return String(value);
39478
39479         // If the type is "object", we might be dealing with an object or an array or
39480         // null.
39481
39482                 case "object":
39483
39484         // Due to a specification blunder in ECMAScript, typeof null is "object",
39485         // so watch out for that case.
39486
39487                     if (!value) {
39488                         return "null";
39489                     }
39490
39491         // Make an array to hold the partial results of stringifying this object value.
39492
39493                     gap += indent;
39494                     partial = [];
39495
39496         // Is the value an array?
39497
39498                     if (Object.prototype.toString.apply(value) === "[object Array]") {
39499
39500         // The value is an array. Stringify every element. Use null as a placeholder
39501         // for non-JSON values.
39502
39503                         length = value.length;
39504                         for (i = 0; i < length; i += 1) {
39505                             partial[i] = str(i, value) || "null";
39506                         }
39507
39508         // Join all of the elements together, separated with commas, and wrap them in
39509         // brackets.
39510
39511                         v = partial.length === 0
39512                             ? "[]"
39513                             : gap
39514                                 ? "[\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "]"
39515                                 : "[" + partial.join(",") + "]";
39516                         gap = mind;
39517                         return v;
39518                     }
39519
39520         // If the replacer is an array, use it to select the members to be stringified.
39521
39522                     if (rep && typeof rep === "object") {
39523                         length = rep.length;
39524                         for (i = 0; i < length; i += 1) {
39525                             if (typeof rep[i] === "string") {
39526                                 k = rep[i];
39527                                 v = str(k, value);
39528                                 if (v) {
39529                                     partial.push(quote(k) + (
39530                                         gap
39531                                             ? ": "
39532                                             : ":"
39533                                     ) + v);
39534                                 }
39535                             }
39536                         }
39537                     } else {
39538
39539         // Otherwise, iterate through all of the keys in the object.
39540
39541                         for (k in value) {
39542                             if (Object.prototype.hasOwnProperty.call(value, k)) {
39543                                 v = str(k, value);
39544                                 if (v) {
39545                                     partial.push(quote(k) + (
39546                                         gap
39547                                             ? ": "
39548                                             : ":"
39549                                     ) + v);
39550                                 }
39551                             }
39552                         }
39553                     }
39554
39555         // Join all of the member texts together, separated with commas,
39556         // and wrap them in braces.
39557
39558                     v = partial.length === 0
39559                         ? "{}"
39560                         : gap
39561                             ? "{\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "}"
39562                             : "{" + partial.join(",") + "}";
39563                     gap = mind;
39564                     return v;
39565                 }
39566             }
39567
39568         // If the JSON object does not yet have a stringify method, give it one.
39569
39570             if (typeof JSON.stringify !== "function") {
39571                 meta = {    // table of character substitutions
39572                     "\b": "\\b",
39573                     "\t": "\\t",
39574                     "\n": "\\n",
39575                     "\f": "\\f",
39576                     "\r": "\\r",
39577                     "\"": "\\\"",
39578                     "\\": "\\\\"
39579                 };
39580                 JSON.stringify = function (value, replacer, space) {
39581
39582         // The stringify method takes a value and an optional replacer, and an optional
39583         // space parameter, and returns a JSON text. The replacer can be a function
39584         // that can replace values, or an array of strings that will select the keys.
39585         // A default replacer method can be provided. Use of the space parameter can
39586         // produce text that is more easily readable.
39587
39588                     var i;
39589                     gap = "";
39590                     indent = "";
39591
39592         // If the space parameter is a number, make an indent string containing that
39593         // many spaces.
39594
39595                     if (typeof space === "number") {
39596                         for (i = 0; i < space; i += 1) {
39597                             indent += " ";
39598                         }
39599
39600         // If the space parameter is a string, it will be used as the indent string.
39601
39602                     } else if (typeof space === "string") {
39603                         indent = space;
39604                     }
39605
39606         // If there is a replacer, it must be a function or an array.
39607         // Otherwise, throw an error.
39608
39609                     rep = replacer;
39610                     if (replacer && typeof replacer !== "function" &&
39611                             (typeof replacer !== "object" ||
39612                             typeof replacer.length !== "number")) {
39613                         throw new Error("JSON.stringify");
39614                     }
39615
39616         // Make a fake root object containing our value under the key of "".
39617         // Return the result of stringifying the value.
39618
39619                     return str("", {"": value});
39620                 };
39621             }
39622
39623
39624         // If the JSON object does not yet have a parse method, give it one.
39625
39626             if (typeof JSON.parse !== "function") {
39627                 JSON.parse = function (text, reviver) {
39628
39629         // The parse method takes a text and an optional reviver function, and returns
39630         // a JavaScript value if the text is a valid JSON text.
39631
39632                     var j;
39633
39634                     function walk(holder, key) {
39635
39636         // The walk method is used to recursively walk the resulting structure so
39637         // that modifications can be made.
39638
39639                         var k;
39640                         var v;
39641                         var value = holder[key];
39642                         if (value && typeof value === "object") {
39643                             for (k in value) {
39644                                 if (Object.prototype.hasOwnProperty.call(value, k)) {
39645                                     v = walk(value, k);
39646                                     if (v !== undefined) {
39647                                         value[k] = v;
39648                                     } else {
39649                                         delete value[k];
39650                                     }
39651                                 }
39652                             }
39653                         }
39654                         return reviver.call(holder, key, value);
39655                     }
39656
39657
39658         // Parsing happens in four stages. In the first stage, we replace certain
39659         // Unicode characters with escape sequences. JavaScript handles many characters
39660         // incorrectly, either silently deleting them, or treating them as line endings.
39661
39662                     text = String(text);
39663                     rx_dangerous.lastIndex = 0;
39664                     if (rx_dangerous.test(text)) {
39665                         text = text.replace(rx_dangerous, function (a) {
39666                             return "\\u" +
39667                                     ("0000" + a.charCodeAt(0).toString(16)).slice(-4);
39668                         });
39669                     }
39670
39671         // In the second stage, we run the text against regular expressions that look
39672         // for non-JSON patterns. We are especially concerned with "()" and "new"
39673         // because they can cause invocation, and "=" because it can cause mutation.
39674         // But just to be safe, we want to reject all unexpected forms.
39675
39676         // We split the second stage into 4 regexp operations in order to work around
39677         // crippling inefficiencies in IE's and Safari's regexp engines. First we
39678         // replace the JSON backslash pairs with "@" (a non-JSON character). Second, we
39679         // replace all simple value tokens with "]" characters. Third, we delete all
39680         // open brackets that follow a colon or comma or that begin the text. Finally,
39681         // we look to see that the remaining characters are only whitespace or "]" or
39682         // "," or ":" or "{" or "}". If that is so, then the text is safe for eval.
39683
39684                     if (
39685                         rx_one.test(
39686                             text
39687                                 .replace(rx_two, "@")
39688                                 .replace(rx_three, "]")
39689                                 .replace(rx_four, "")
39690                         )
39691                     ) {
39692
39693         // In the third stage we use the eval function to compile the text into a
39694         // JavaScript structure. The "{" operator is subject to a syntactic ambiguity
39695         // in JavaScript: it can begin a block or an object literal. We wrap the text
39696         // in parens to eliminate the ambiguity.
39697
39698                         j = eval("(" + text + ")");
39699
39700         // In the optional fourth stage, we recursively walk the new structure, passing
39701         // each name/value pair to a reviver function for possible transformation.
39702
39703                         return (typeof reviver === "function")
39704                             ? walk({"": j}, "")
39705                             : j;
39706                     }
39707
39708         // If the text is not JSON parseable, then a SyntaxError is thrown.
39709
39710                     throw new SyntaxError("JSON.parse");
39711                 };
39712             }
39713         }());
39714
39715         var json2 = json2Plugin;
39716
39717         function json2Plugin() {
39718                 
39719                 return {}
39720         }
39721
39722         var plugins = [json2];
39723
39724         var store_legacy = storeEngine.createStore(all, plugins);
39725
39726         // # osm-auth
39727         //
39728         // This code is only compatible with IE10+ because the [XDomainRequest](http://bit.ly/LfO7xo)
39729         // object, IE<10's idea of [CORS](http://en.wikipedia.org/wiki/Cross-origin_resource_sharing),
39730         // does not support custom headers, which this uses everywhere.
39731         var osmAuth = function(o) {
39732
39733             var oauth = {};
39734
39735             // authenticated users will also have a request token secret, but it's
39736             // not used in transactions with the server
39737             oauth.authenticated = function() {
39738                 return !!(token('oauth_token') && token('oauth_token_secret'));
39739             };
39740
39741             oauth.logout = function() {
39742                 token('oauth_token', '');
39743                 token('oauth_token_secret', '');
39744                 token('oauth_request_token_secret', '');
39745                 return oauth;
39746             };
39747
39748             // TODO: detect lack of click event
39749             oauth.authenticate = function(callback) {
39750                 if (oauth.authenticated()) { return callback(); }
39751
39752                 oauth.logout();
39753
39754                 // ## Getting a request token
39755                 var params = timenonce(getAuth(o)),
39756                     url = o.url + '/oauth/request_token';
39757
39758                 params.oauth_signature = ohauth_1.signature(
39759                     o.oauth_secret, '',
39760                     ohauth_1.baseString('POST', url, params));
39761
39762                 if (!o.singlepage) {
39763                     // Create a 600x550 popup window in the center of the screen
39764                     var w = 600, h = 550,
39765                         settings = [
39766                             ['width', w], ['height', h],
39767                             ['left', screen.width / 2 - w / 2],
39768                             ['top', screen.height / 2 - h / 2]].map(function(x) {
39769                                 return x.join('=');
39770                             }).join(','),
39771                         popup = window.open('about:blank', 'oauth_window', settings);
39772                 }
39773
39774                 // Request a request token. When this is complete, the popup
39775                 // window is redirected to OSM's authorization page.
39776                 ohauth_1.xhr('POST', url, params, null, {}, reqTokenDone);
39777                 o.loading();
39778
39779                 function reqTokenDone(err, xhr) {
39780                     o.done();
39781                     if (err) { return callback(err); }
39782                     var resp = ohauth_1.stringQs(xhr.response);
39783                     token('oauth_request_token_secret', resp.oauth_token_secret);
39784                     var authorize_url = o.url + '/oauth/authorize?' + ohauth_1.qsString({
39785                         oauth_token: resp.oauth_token,
39786                         oauth_callback: resolveUrl$1(o.landing)
39787                     });
39788
39789                     if (o.singlepage) {
39790                         location.href = authorize_url;
39791                     } else {
39792                         popup.location = authorize_url;
39793                     }
39794                 }
39795
39796                 // Called by a function in a landing page, in the popup window. The
39797                 // window closes itself.
39798                 window.authComplete = function(token) {
39799                     var oauth_token = ohauth_1.stringQs(token.split('?')[1]);
39800                     get_access_token(oauth_token.oauth_token);
39801                     delete window.authComplete;
39802                 };
39803
39804                 // ## Getting an request token
39805                 //
39806                 // At this point we have an `oauth_token`, brought in from a function
39807                 // call on a landing page popup.
39808                 function get_access_token(oauth_token) {
39809                     var url = o.url + '/oauth/access_token',
39810                         params = timenonce(getAuth(o)),
39811                         request_token_secret = token('oauth_request_token_secret');
39812                     params.oauth_token = oauth_token;
39813                     params.oauth_signature = ohauth_1.signature(
39814                         o.oauth_secret,
39815                         request_token_secret,
39816                         ohauth_1.baseString('POST', url, params));
39817
39818                     // ## Getting an access token
39819                     //
39820                     // The final token required for authentication. At this point
39821                     // we have a `request token secret`
39822                     ohauth_1.xhr('POST', url, params, null, {}, accessTokenDone);
39823                     o.loading();
39824                 }
39825
39826                 function accessTokenDone(err, xhr) {
39827                     o.done();
39828                     if (err) { return callback(err); }
39829                     var access_token = ohauth_1.stringQs(xhr.response);
39830                     token('oauth_token', access_token.oauth_token);
39831                     token('oauth_token_secret', access_token.oauth_token_secret);
39832                     callback(null, oauth);
39833                 }
39834             };
39835
39836             oauth.bootstrapToken = function(oauth_token, callback) {
39837                 // ## Getting an request token
39838                 // At this point we have an `oauth_token`, brought in from a function
39839                 // call on a landing page popup.
39840                 function get_access_token(oauth_token) {
39841                     var url = o.url + '/oauth/access_token',
39842                         params = timenonce(getAuth(o)),
39843                         request_token_secret = token('oauth_request_token_secret');
39844                     params.oauth_token = oauth_token;
39845                     params.oauth_signature = ohauth_1.signature(
39846                         o.oauth_secret,
39847                         request_token_secret,
39848                         ohauth_1.baseString('POST', url, params));
39849
39850                     // ## Getting an access token
39851                     // The final token required for authentication. At this point
39852                     // we have a `request token secret`
39853                     ohauth_1.xhr('POST', url, params, null, {}, accessTokenDone);
39854                     o.loading();
39855                 }
39856
39857                 function accessTokenDone(err, xhr) {
39858                     o.done();
39859                     if (err) { return callback(err); }
39860                     var access_token = ohauth_1.stringQs(xhr.response);
39861                     token('oauth_token', access_token.oauth_token);
39862                     token('oauth_token_secret', access_token.oauth_token_secret);
39863                     callback(null, oauth);
39864                 }
39865
39866                 get_access_token(oauth_token);
39867             };
39868
39869             // # xhr
39870             //
39871             // A single XMLHttpRequest wrapper that does authenticated calls if the
39872             // user has logged in.
39873             oauth.xhr = function(options, callback) {
39874                 if (!oauth.authenticated()) {
39875                     if (o.auto) {
39876                         return oauth.authenticate(run);
39877                     } else {
39878                         callback('not authenticated', null);
39879                         return;
39880                     }
39881                 } else {
39882                     return run();
39883                 }
39884
39885                 function run() {
39886                     var params = timenonce(getAuth(o)),
39887                         oauth_token_secret = token('oauth_token_secret'),
39888                         url = (options.prefix !== false) ? o.url + options.path : options.path,
39889                         url_parts = url.replace(/#.*$/, '').split('?', 2),
39890                         base_url = url_parts[0],
39891                         query = (url_parts.length === 2) ? url_parts[1] : '';
39892
39893                     // https://tools.ietf.org/html/rfc5849#section-3.4.1.3.1
39894                     if ((!options.options || !options.options.header ||
39895                         options.options.header['Content-Type'] === 'application/x-www-form-urlencoded') &&
39896                         options.content) {
39897                         params = immutable(params, ohauth_1.stringQs(options.content));
39898                     }
39899
39900                     params.oauth_token = token('oauth_token');
39901                     params.oauth_signature = ohauth_1.signature(
39902                         o.oauth_secret,
39903                         oauth_token_secret,
39904                         ohauth_1.baseString(options.method, base_url, immutable(params, ohauth_1.stringQs(query)))
39905                     );
39906
39907                     return ohauth_1.xhr(options.method, url, params, options.content, options.options, done);
39908                 }
39909
39910                 function done(err, xhr) {
39911                     if (err) { return callback(err); }
39912                     else if (xhr.responseXML) { return callback(err, xhr.responseXML); }
39913                     else { return callback(err, xhr.response); }
39914                 }
39915             };
39916
39917             // pre-authorize this object, if we can just get a token and token_secret
39918             // from the start
39919             oauth.preauth = function(c) {
39920                 if (!c) { return; }
39921                 if (c.oauth_token) { token('oauth_token', c.oauth_token); }
39922                 if (c.oauth_token_secret) { token('oauth_token_secret', c.oauth_token_secret); }
39923                 return oauth;
39924             };
39925
39926             oauth.options = function(_) {
39927                 if (!arguments.length) { return o; }
39928
39929                 o = _;
39930                 o.url = o.url || 'https://www.openstreetmap.org';
39931                 o.landing = o.landing || 'land.html';
39932                 o.singlepage = o.singlepage || false;
39933
39934                 // Optional loading and loading-done functions for nice UI feedback.
39935                 // by default, no-ops
39936                 o.loading = o.loading || function() {};
39937                 o.done = o.done || function() {};
39938
39939                 return oauth.preauth(o);
39940             };
39941
39942             // 'stamp' an authentication object from `getAuth()`
39943             // with a [nonce](http://en.wikipedia.org/wiki/Cryptographic_nonce)
39944             // and timestamp
39945             function timenonce(o) {
39946                 o.oauth_timestamp = ohauth_1.timestamp();
39947                 o.oauth_nonce = ohauth_1.nonce();
39948                 return o;
39949             }
39950
39951             // get/set tokens. These are prefixed with the base URL so that `osm-auth`
39952             // can be used with multiple APIs and the keys in `localStorage`
39953             // will not clash
39954             var token;
39955
39956             if (store_legacy.enabled) {
39957                 token = function (x, y) {
39958                     if (arguments.length === 1) { return store_legacy.get(o.url + x); }
39959                     else if (arguments.length === 2) { return store_legacy.set(o.url + x, y); }
39960                 };
39961             } else {
39962                 var storage = {};
39963                 token = function (x, y) {
39964                     if (arguments.length === 1) { return storage[o.url + x]; }
39965                     else if (arguments.length === 2) { return storage[o.url + x] = y; }
39966                 };
39967             }
39968
39969             // Get an authentication object. If you just add and remove properties
39970             // from a single object, you'll need to use `delete` to make sure that
39971             // it doesn't contain undesired properties for authentication
39972             function getAuth(o) {
39973                 return {
39974                     oauth_consumer_key: o.oauth_consumer_key,
39975                     oauth_signature_method: 'HMAC-SHA1'
39976                 };
39977             }
39978
39979             // potentially pre-authorize
39980             oauth.options(o);
39981
39982             return oauth;
39983         };
39984
39985         var JXON = new (function () {
39986           var
39987             sValueProp = 'keyValue', sAttributesProp = 'keyAttributes', sAttrPref = '@', /* you can customize these values */
39988             aCache = [], rIsNull = /^\s*$/, rIsBool = /^(?:true|false)$/i;
39989
39990           function parseText (sValue) {
39991             if (rIsNull.test(sValue)) { return null; }
39992             if (rIsBool.test(sValue)) { return sValue.toLowerCase() === 'true'; }
39993             if (isFinite(sValue)) { return parseFloat(sValue); }
39994             if (isFinite(Date.parse(sValue))) { return new Date(sValue); }
39995             return sValue;
39996           }
39997
39998           function EmptyTree () { }
39999           EmptyTree.prototype.toString = function () { return 'null'; };
40000           EmptyTree.prototype.valueOf = function () { return null; };
40001
40002           function objectify (vValue) {
40003             return vValue === null ? new EmptyTree() : vValue instanceof Object ? vValue : new vValue.constructor(vValue);
40004           }
40005
40006           function createObjTree (oParentNode, nVerb, bFreeze, bNesteAttr) {
40007             var
40008               nLevelStart = aCache.length, bChildren = oParentNode.hasChildNodes(),
40009               bAttributes = oParentNode.hasAttributes(), bHighVerb = Boolean(nVerb & 2);
40010
40011             var
40012               sProp, vContent, nLength = 0, sCollectedTxt = '',
40013               vResult = bHighVerb ? {} : /* put here the default value for empty nodes: */ true;
40014
40015             if (bChildren) {
40016               for (var oNode, nItem = 0; nItem < oParentNode.childNodes.length; nItem++) {
40017                 oNode = oParentNode.childNodes.item(nItem);
40018                 if (oNode.nodeType === 4) { sCollectedTxt += oNode.nodeValue; } /* nodeType is 'CDATASection' (4) */
40019                 else if (oNode.nodeType === 3) { sCollectedTxt += oNode.nodeValue.trim(); } /* nodeType is 'Text' (3) */
40020                 else if (oNode.nodeType === 1 && !oNode.prefix) { aCache.push(oNode); } /* nodeType is 'Element' (1) */
40021               }
40022             }
40023
40024             var nLevelEnd = aCache.length, vBuiltVal = parseText(sCollectedTxt);
40025
40026             if (!bHighVerb && (bChildren || bAttributes)) { vResult = nVerb === 0 ? objectify(vBuiltVal) : {}; }
40027
40028             for (var nElId = nLevelStart; nElId < nLevelEnd; nElId++) {
40029               sProp = aCache[nElId].nodeName.toLowerCase();
40030               vContent = createObjTree(aCache[nElId], nVerb, bFreeze, bNesteAttr);
40031               if (vResult.hasOwnProperty(sProp)) {
40032                 if (vResult[sProp].constructor !== Array) { vResult[sProp] = [vResult[sProp]]; }
40033                 vResult[sProp].push(vContent);
40034               } else {
40035                 vResult[sProp] = vContent;
40036                 nLength++;
40037               }
40038             }
40039
40040             if (bAttributes) {
40041               var
40042                 nAttrLen = oParentNode.attributes.length,
40043                 sAPrefix = bNesteAttr ? '' : sAttrPref, oAttrParent = bNesteAttr ? {} : vResult;
40044
40045               for (var oAttrib, nAttrib = 0; nAttrib < nAttrLen; nLength++, nAttrib++) {
40046                 oAttrib = oParentNode.attributes.item(nAttrib);
40047                 oAttrParent[sAPrefix + oAttrib.name.toLowerCase()] = parseText(oAttrib.value.trim());
40048               }
40049
40050               if (bNesteAttr) {
40051                 if (bFreeze) { Object.freeze(oAttrParent); }
40052                 vResult[sAttributesProp] = oAttrParent;
40053                 nLength -= nAttrLen - 1;
40054               }
40055             }
40056
40057             if (nVerb === 3 || (nVerb === 2 || nVerb === 1 && nLength > 0) && sCollectedTxt) {
40058               vResult[sValueProp] = vBuiltVal;
40059             } else if (!bHighVerb && nLength === 0 && sCollectedTxt) {
40060               vResult = vBuiltVal;
40061             }
40062
40063             if (bFreeze && (bHighVerb || nLength > 0)) { Object.freeze(vResult); }
40064
40065             aCache.length = nLevelStart;
40066
40067             return vResult;
40068           }
40069
40070           function loadObjTree (oXMLDoc, oParentEl, oParentObj) {
40071             var vValue, oChild;
40072
40073             if (oParentObj instanceof String || oParentObj instanceof Number || oParentObj instanceof Boolean) {
40074               oParentEl.appendChild(oXMLDoc.createTextNode(oParentObj.toString())); /* verbosity level is 0 */
40075             } else if (oParentObj.constructor === Date) {
40076               oParentEl.appendChild(oXMLDoc.createTextNode(oParentObj.toGMTString()));    
40077             }
40078
40079             for (var sName in oParentObj) {
40080               vValue = oParentObj[sName];
40081               if (isFinite(sName) || vValue instanceof Function) { continue; } /* verbosity level is 0 */
40082               if (sName === sValueProp) {
40083                 if (vValue !== null && vValue !== true) { oParentEl.appendChild(oXMLDoc.createTextNode(vValue.constructor === Date ? vValue.toGMTString() : String(vValue))); }
40084               } else if (sName === sAttributesProp) { /* verbosity level is 3 */
40085                 for (var sAttrib in vValue) { oParentEl.setAttribute(sAttrib, vValue[sAttrib]); }
40086               } else if (sName.charAt(0) === sAttrPref) {
40087                 oParentEl.setAttribute(sName.slice(1), vValue);
40088               } else if (vValue.constructor === Array) {
40089                 for (var nItem = 0; nItem < vValue.length; nItem++) {
40090                   oChild = oXMLDoc.createElement(sName);
40091                   loadObjTree(oXMLDoc, oChild, vValue[nItem]);
40092                   oParentEl.appendChild(oChild);
40093                 }
40094               } else {
40095                 oChild = oXMLDoc.createElement(sName);
40096                 if (vValue instanceof Object) {
40097                   loadObjTree(oXMLDoc, oChild, vValue);
40098                 } else if (vValue !== null && vValue !== true) {
40099                   oChild.appendChild(oXMLDoc.createTextNode(vValue.toString()));
40100                 }
40101                 oParentEl.appendChild(oChild);
40102              }
40103            }
40104           }
40105
40106           this.build = function (oXMLParent, nVerbosity /* optional */, bFreeze /* optional */, bNesteAttributes /* optional */) {
40107             var _nVerb = arguments.length > 1 && typeof nVerbosity === 'number' ? nVerbosity & 3 : /* put here the default verbosity level: */ 1;
40108             return createObjTree(oXMLParent, _nVerb, bFreeze || false, arguments.length > 3 ? bNesteAttributes : _nVerb === 3);    
40109           };
40110
40111           this.unbuild = function (oObjTree) {    
40112             var oNewDoc = document.implementation.createDocument('', '', null);
40113             loadObjTree(oNewDoc, oNewDoc, oObjTree);
40114             return oNewDoc;
40115           };
40116
40117           this.stringify = function (oObjTree) {
40118             return (new XMLSerializer()).serializeToString(JXON.unbuild(oObjTree));
40119           };
40120         })();
40121
40122         // var myObject = JXON.build(doc);
40123         // we got our javascript object! try: alert(JSON.stringify(myObject));
40124
40125         // var newDoc = JXON.unbuild(myObject);
40126         // we got our Document instance! try: alert((new XMLSerializer()).serializeToString(newDoc));
40127
40128         var tiler$5 = utilTiler();
40129         var dispatch$6 = dispatch('apiStatusChange', 'authLoading', 'authDone', 'change', 'loading', 'loaded', 'loadedNotes');
40130         var urlroot = 'https://www.openstreetmap.org';
40131         var oauth = osmAuth({
40132             url: urlroot,
40133             oauth_consumer_key: '5A043yRSEugj4DJ5TljuapfnrflWDte8jTOcWLlT',
40134             oauth_secret: 'aB3jKq1TRsCOUrfOIZ6oQMEDmv2ptV76PA54NGLL',
40135             loading: authLoading,
40136             done: authDone
40137         });
40138
40139         var _blacklists = ['.*\.google(apis)?\..*/(vt|kh)[\?/].*([xyz]=.*){3}.*'];
40140         var _tileCache = { toLoad: {}, loaded: {}, inflight: {}, seen: {}, rtree: new RBush() };
40141         var _noteCache = { toLoad: {}, loaded: {}, inflight: {}, inflightPost: {}, note: {}, closed: {}, rtree: new RBush() };
40142         var _userCache = { toLoad: {}, user: {} };
40143         var _cachedApiStatus;
40144         var _changeset = {};
40145
40146         var _deferred = new Set();
40147         var _connectionID = 1;
40148         var _tileZoom$3 = 16;
40149         var _noteZoom = 12;
40150         var _rateLimitError;
40151         var _userChangesets;
40152         var _userDetails;
40153         var _off;
40154
40155         // set a default but also load this from the API status
40156         var _maxWayNodes = 2000;
40157
40158
40159         function authLoading() {
40160             dispatch$6.call('authLoading');
40161         }
40162
40163
40164         function authDone() {
40165             dispatch$6.call('authDone');
40166         }
40167
40168
40169         function abortRequest$5(controllerOrXHR) {
40170             if (controllerOrXHR) {
40171                 controllerOrXHR.abort();
40172             }
40173         }
40174
40175
40176         function hasInflightRequests(cache) {
40177             return Object.keys(cache.inflight).length;
40178         }
40179
40180
40181         function abortUnwantedRequests$3(cache, visibleTiles) {
40182             Object.keys(cache.inflight).forEach(function(k) {
40183                 if (cache.toLoad[k]) { return; }
40184                 if (visibleTiles.find(function(tile) { return k === tile.id; })) { return; }
40185
40186                 abortRequest$5(cache.inflight[k]);
40187                 delete cache.inflight[k];
40188             });
40189         }
40190
40191
40192         function getLoc(attrs) {
40193             var lon = attrs.lon && attrs.lon.value;
40194             var lat = attrs.lat && attrs.lat.value;
40195             return [parseFloat(lon), parseFloat(lat)];
40196         }
40197
40198
40199         function getNodes(obj) {
40200             var elems = obj.getElementsByTagName('nd');
40201             var nodes = new Array(elems.length);
40202             for (var i = 0, l = elems.length; i < l; i++) {
40203                 nodes[i] = 'n' + elems[i].attributes.ref.value;
40204             }
40205             return nodes;
40206         }
40207
40208         function getNodesJSON(obj) {
40209             var elems = obj.nodes;
40210             var nodes = new Array(elems.length);
40211             for (var i = 0, l = elems.length; i < l; i++) {
40212                 nodes[i] = 'n' + elems[i];
40213             }
40214             return nodes;
40215         }
40216
40217         function getTags(obj) {
40218             var elems = obj.getElementsByTagName('tag');
40219             var tags = {};
40220             for (var i = 0, l = elems.length; i < l; i++) {
40221                 var attrs = elems[i].attributes;
40222                 tags[attrs.k.value] = attrs.v.value;
40223             }
40224
40225             return tags;
40226         }
40227
40228
40229         function getMembers(obj) {
40230             var elems = obj.getElementsByTagName('member');
40231             var members = new Array(elems.length);
40232             for (var i = 0, l = elems.length; i < l; i++) {
40233                 var attrs = elems[i].attributes;
40234                 members[i] = {
40235                     id: attrs.type.value[0] + attrs.ref.value,
40236                     type: attrs.type.value,
40237                     role: attrs.role.value
40238                 };
40239             }
40240             return members;
40241         }
40242
40243         function getMembersJSON(obj) {
40244             var elems = obj.members;
40245             var members = new Array(elems.length);
40246             for (var i = 0, l = elems.length; i < l; i++) {
40247                 var attrs = elems[i];
40248                 members[i] = {
40249                     id: attrs.type[0] + attrs.ref,
40250                     type: attrs.type,
40251                     role: attrs.role
40252                 };
40253             }
40254             return members;
40255         }
40256
40257         function getVisible(attrs) {
40258             return (!attrs.visible || attrs.visible.value !== 'false');
40259         }
40260
40261
40262         function parseComments(comments) {
40263             var parsedComments = [];
40264
40265             // for each comment
40266             for (var i = 0; i < comments.length; i++) {
40267                 var comment = comments[i];
40268                 if (comment.nodeName === 'comment') {
40269                     var childNodes = comment.childNodes;
40270                     var parsedComment = {};
40271
40272                     for (var j = 0; j < childNodes.length; j++) {
40273                         var node = childNodes[j];
40274                         var nodeName = node.nodeName;
40275                         if (nodeName === '#text') { continue; }
40276                         parsedComment[nodeName] = node.textContent;
40277
40278                         if (nodeName === 'uid') {
40279                             var uid = node.textContent;
40280                             if (uid && !_userCache.user[uid]) {
40281                                 _userCache.toLoad[uid] = true;
40282                             }
40283                         }
40284                     }
40285
40286                     if (parsedComment) {
40287                         parsedComments.push(parsedComment);
40288                     }
40289                 }
40290             }
40291             return parsedComments;
40292         }
40293
40294
40295         function encodeNoteRtree(note) {
40296             return {
40297                 minX: note.loc[0],
40298                 minY: note.loc[1],
40299                 maxX: note.loc[0],
40300                 maxY: note.loc[1],
40301                 data: note
40302             };
40303         }
40304
40305
40306         var jsonparsers = {
40307
40308             node: function nodeData(obj, uid) {
40309                 return new osmNode({
40310                     id:  uid,
40311                     visible: typeof obj.visible === 'boolean' ? obj.visible : true,
40312                     version: obj.version.toString(),
40313                     changeset: obj.changeset.toString(),
40314                     timestamp: obj.timestamp,
40315                     user: obj.user,
40316                     uid: obj.uid.toString(),
40317                     loc: [parseFloat(obj.lon), parseFloat(obj.lat)],
40318                     tags: obj.tags
40319                 });
40320             },
40321
40322             way: function wayData(obj, uid) {
40323                 return new osmWay({
40324                     id:  uid,
40325                     visible: typeof obj.visible === 'boolean' ? obj.visible : true,
40326                     version: obj.version.toString(),
40327                     changeset: obj.changeset.toString(),
40328                     timestamp: obj.timestamp,
40329                     user: obj.user,
40330                     uid: obj.uid.toString(),
40331                     tags: obj.tags,
40332                     nodes: getNodesJSON(obj)
40333                 });
40334             },
40335
40336             relation: function relationData(obj, uid) {
40337                 return new osmRelation({
40338                     id:  uid,
40339                     visible: typeof obj.visible === 'boolean' ? obj.visible : true,
40340                     version: obj.version.toString(),
40341                     changeset: obj.changeset.toString(),
40342                     timestamp: obj.timestamp,
40343                     user: obj.user,
40344                     uid: obj.uid.toString(),
40345                     tags: obj.tags,
40346                     members: getMembersJSON(obj)
40347                 });
40348             }
40349         };
40350
40351         function parseJSON(payload, callback, options) {
40352             options = Object.assign({ skipSeen: true }, options);
40353             if (!payload)  {
40354                 return callback({ message: 'No JSON', status: -1 });
40355             }
40356
40357             var json = payload;
40358             if (typeof json !== 'object')
40359                { json = JSON.parse(payload); }
40360
40361             if (!json.elements)
40362                 { return callback({ message: 'No JSON', status: -1 }); }
40363
40364             var children = json.elements;
40365
40366             var handle = window.requestIdleCallback(function() {
40367                 var results = [];
40368                 var result;
40369                 for (var i = 0; i < children.length; i++) {
40370                     result = parseChild(children[i]);
40371                     if (result) { results.push(result); }
40372                 }
40373                 callback(null, results);
40374             });
40375
40376             _deferred.add(handle);
40377
40378             function parseChild(child) {
40379                 var parser = jsonparsers[child.type];
40380                 if (!parser) { return null; }
40381
40382                 var uid;
40383
40384                 uid = osmEntity.id.fromOSM(child.type, child.id);
40385                 if (options.skipSeen) {
40386                     if (_tileCache.seen[uid]) { return null; }  // avoid reparsing a "seen" entity
40387                     _tileCache.seen[uid] = true;
40388                 }
40389
40390                 return parser(child, uid);
40391             }
40392         }
40393
40394         var parsers = {
40395             node: function nodeData(obj, uid) {
40396                 var attrs = obj.attributes;
40397                 return new osmNode({
40398                     id: uid,
40399                     visible: getVisible(attrs),
40400                     version: attrs.version.value,
40401                     changeset: attrs.changeset && attrs.changeset.value,
40402                     timestamp: attrs.timestamp && attrs.timestamp.value,
40403                     user: attrs.user && attrs.user.value,
40404                     uid: attrs.uid && attrs.uid.value,
40405                     loc: getLoc(attrs),
40406                     tags: getTags(obj)
40407                 });
40408             },
40409
40410             way: function wayData(obj, uid) {
40411                 var attrs = obj.attributes;
40412                 return new osmWay({
40413                     id: uid,
40414                     visible: getVisible(attrs),
40415                     version: attrs.version.value,
40416                     changeset: attrs.changeset && attrs.changeset.value,
40417                     timestamp: attrs.timestamp && attrs.timestamp.value,
40418                     user: attrs.user && attrs.user.value,
40419                     uid: attrs.uid && attrs.uid.value,
40420                     tags: getTags(obj),
40421                     nodes: getNodes(obj),
40422                 });
40423             },
40424
40425             relation: function relationData(obj, uid) {
40426                 var attrs = obj.attributes;
40427                 return new osmRelation({
40428                     id: uid,
40429                     visible: getVisible(attrs),
40430                     version: attrs.version.value,
40431                     changeset: attrs.changeset && attrs.changeset.value,
40432                     timestamp: attrs.timestamp && attrs.timestamp.value,
40433                     user: attrs.user && attrs.user.value,
40434                     uid: attrs.uid && attrs.uid.value,
40435                     tags: getTags(obj),
40436                     members: getMembers(obj)
40437                 });
40438             },
40439
40440             note: function parseNote(obj, uid) {
40441                 var attrs = obj.attributes;
40442                 var childNodes = obj.childNodes;
40443                 var props = {};
40444
40445                 props.id = uid;
40446                 props.loc = getLoc(attrs);
40447
40448                 // if notes are coincident, move them apart slightly
40449                 var coincident = false;
40450                 var epsilon = 0.00001;
40451                 do {
40452                     if (coincident) {
40453                         props.loc = geoVecAdd(props.loc, [epsilon, epsilon]);
40454                     }
40455                     var bbox = geoExtent(props.loc).bbox();
40456                     coincident = _noteCache.rtree.search(bbox).length;
40457                 } while (coincident);
40458
40459                 // parse note contents
40460                 for (var i = 0; i < childNodes.length; i++) {
40461                     var node = childNodes[i];
40462                     var nodeName = node.nodeName;
40463                     if (nodeName === '#text') { continue; }
40464
40465                     // if the element is comments, parse the comments
40466                     if (nodeName === 'comments') {
40467                         props[nodeName] = parseComments(node.childNodes);
40468                     } else {
40469                         props[nodeName] = node.textContent;
40470                     }
40471                 }
40472
40473                 var note = new osmNote(props);
40474                 var item = encodeNoteRtree(note);
40475                 _noteCache.note[note.id] = note;
40476                 _noteCache.rtree.insert(item);
40477
40478                 return note;
40479             },
40480
40481             user: function parseUser(obj, uid) {
40482                 var attrs = obj.attributes;
40483                 var user = {
40484                     id: uid,
40485                     display_name: attrs.display_name && attrs.display_name.value,
40486                     account_created: attrs.account_created && attrs.account_created.value,
40487                     changesets_count: '0',
40488                     active_blocks: '0'
40489                 };
40490
40491                 var img = obj.getElementsByTagName('img');
40492                 if (img && img[0] && img[0].getAttribute('href')) {
40493                     user.image_url = img[0].getAttribute('href');
40494                 }
40495
40496                 var changesets = obj.getElementsByTagName('changesets');
40497                 if (changesets && changesets[0] && changesets[0].getAttribute('count')) {
40498                     user.changesets_count = changesets[0].getAttribute('count');
40499                 }
40500
40501                 var blocks = obj.getElementsByTagName('blocks');
40502                 if (blocks && blocks[0]) {
40503                     var received = blocks[0].getElementsByTagName('received');
40504                     if (received && received[0] && received[0].getAttribute('active')) {
40505                         user.active_blocks = received[0].getAttribute('active');
40506                     }
40507                 }
40508
40509                 _userCache.user[uid] = user;
40510                 delete _userCache.toLoad[uid];
40511                 return user;
40512             }
40513         };
40514
40515
40516         function parseXML(xml, callback, options) {
40517             options = Object.assign({ skipSeen: true }, options);
40518             if (!xml || !xml.childNodes) {
40519                 return callback({ message: 'No XML', status: -1 });
40520             }
40521
40522             var root = xml.childNodes[0];
40523             var children = root.childNodes;
40524
40525             var handle = window.requestIdleCallback(function() {
40526                 var results = [];
40527                 var result;
40528                 for (var i = 0; i < children.length; i++) {
40529                     result = parseChild(children[i]);
40530                     if (result) { results.push(result); }
40531                 }
40532                 callback(null, results);
40533             });
40534
40535             _deferred.add(handle);
40536
40537
40538             function parseChild(child) {
40539                 var parser = parsers[child.nodeName];
40540                 if (!parser) { return null; }
40541
40542                 var uid;
40543                 if (child.nodeName === 'user') {
40544                     uid = child.attributes.id.value;
40545                     if (options.skipSeen && _userCache.user[uid]) {
40546                         delete _userCache.toLoad[uid];
40547                         return null;
40548                     }
40549
40550                 } else if (child.nodeName === 'note') {
40551                     uid = child.getElementsByTagName('id')[0].textContent;
40552
40553                 } else {
40554                     uid = osmEntity.id.fromOSM(child.nodeName, child.attributes.id.value);
40555                     if (options.skipSeen) {
40556                         if (_tileCache.seen[uid]) { return null; }  // avoid reparsing a "seen" entity
40557                         _tileCache.seen[uid] = true;
40558                     }
40559                 }
40560
40561                 return parser(child, uid);
40562             }
40563         }
40564
40565
40566         // replace or remove note from rtree
40567         function updateRtree$3(item, replace) {
40568             _noteCache.rtree.remove(item, function isEql(a, b) { return a.data.id === b.data.id; });
40569
40570             if (replace) {
40571                 _noteCache.rtree.insert(item);
40572             }
40573         }
40574
40575
40576         function wrapcb(thisArg, callback, cid) {
40577             return function(err, result) {
40578                 if (err) {
40579                     // 400 Bad Request, 401 Unauthorized, 403 Forbidden..
40580                     if (err.status === 400 || err.status === 401 || err.status === 403) {
40581                         thisArg.logout();
40582                     }
40583                     return callback.call(thisArg, err);
40584
40585                 } else if (thisArg.getConnectionId() !== cid) {
40586                     return callback.call(thisArg, { message: 'Connection Switched', status: -1 });
40587
40588                 } else {
40589                     return callback.call(thisArg, err, result);
40590                 }
40591             };
40592         }
40593
40594
40595         var serviceOsm = {
40596
40597             init: function() {
40598                 utilRebind(this, dispatch$6, 'on');
40599             },
40600
40601
40602             reset: function() {
40603                 Array.from(_deferred).forEach(function(handle) {
40604                     window.cancelIdleCallback(handle);
40605                     _deferred.delete(handle);
40606                 });
40607
40608                 _connectionID++;
40609                 _userChangesets = undefined;
40610                 _userDetails = undefined;
40611                 _rateLimitError = undefined;
40612
40613                 Object.values(_tileCache.inflight).forEach(abortRequest$5);
40614                 Object.values(_noteCache.inflight).forEach(abortRequest$5);
40615                 Object.values(_noteCache.inflightPost).forEach(abortRequest$5);
40616                 if (_changeset.inflight) { abortRequest$5(_changeset.inflight); }
40617
40618                 _tileCache = { toLoad: {}, loaded: {}, inflight: {}, seen: {}, rtree: new RBush() };
40619                 _noteCache = { toLoad: {}, loaded: {}, inflight: {}, inflightPost: {}, note: {}, closed: {}, rtree: new RBush() };
40620                 _userCache = { toLoad: {}, user: {} };
40621                 _cachedApiStatus = undefined;
40622                 _changeset = {};
40623
40624                 return this;
40625             },
40626
40627
40628             getConnectionId: function() {
40629                 return _connectionID;
40630             },
40631
40632
40633             changesetURL: function(changesetID) {
40634                 return urlroot + '/changeset/' + changesetID;
40635             },
40636
40637
40638             changesetsURL: function(center, zoom) {
40639                 var precision = Math.max(0, Math.ceil(Math.log(zoom) / Math.LN2));
40640                 return urlroot + '/history#map=' +
40641                     Math.floor(zoom) + '/' +
40642                     center[1].toFixed(precision) + '/' +
40643                     center[0].toFixed(precision);
40644             },
40645
40646
40647             entityURL: function(entity) {
40648                 return urlroot + '/' + entity.type + '/' + entity.osmId();
40649             },
40650
40651
40652             historyURL: function(entity) {
40653                 return urlroot + '/' + entity.type + '/' + entity.osmId() + '/history';
40654             },
40655
40656
40657             userURL: function(username) {
40658                 return urlroot + '/user/' + username;
40659             },
40660
40661
40662             noteURL: function(note) {
40663                 return urlroot + '/note/' + note.id;
40664             },
40665
40666
40667             noteReportURL: function(note) {
40668                 return urlroot + '/reports/new?reportable_type=Note&reportable_id=' + note.id;
40669             },
40670
40671
40672             // Generic method to load data from the OSM API
40673             // Can handle either auth or unauth calls.
40674             loadFromAPI: function(path, callback, options) {
40675                 options = Object.assign({ skipSeen: true }, options);
40676                 var that = this;
40677                 var cid = _connectionID;
40678
40679                 function done(err, payload) {
40680                     if (that.getConnectionId() !== cid) {
40681                         if (callback) { callback({ message: 'Connection Switched', status: -1 }); }
40682                         return;
40683                     }
40684
40685                     var isAuthenticated = that.authenticated();
40686
40687                     // 400 Bad Request, 401 Unauthorized, 403 Forbidden
40688                     // Logout and retry the request..
40689                     if (isAuthenticated && err && err.status &&
40690                             (err.status === 400 || err.status === 401 || err.status === 403)) {
40691                         that.logout();
40692                         that.loadFromAPI(path, callback, options);
40693
40694                     // else, no retry..
40695                     } else {
40696                         // 509 Bandwidth Limit Exceeded, 429 Too Many Requests
40697                         // Set the rateLimitError flag and trigger a warning..
40698                         if (!isAuthenticated && !_rateLimitError && err && err.status &&
40699                                 (err.status === 509 || err.status === 429)) {
40700                             _rateLimitError = err;
40701                             dispatch$6.call('change');
40702                             that.reloadApiStatus();
40703
40704                         } else if ((err && _cachedApiStatus === 'online') ||
40705                             (!err && _cachedApiStatus !== 'online')) {
40706                             // If the response's error state doesn't match the status,
40707                             // it's likely we lost or gained the connection so reload the status
40708                             that.reloadApiStatus();
40709                         }
40710
40711                         if (callback) {
40712                             if (err) {
40713                                 return callback(err);
40714                             } else {
40715                                 if (path.indexOf('.json') !== -1) {
40716                                     return parseJSON(payload, callback, options);
40717                                 } else {
40718                                     return parseXML(payload, callback, options);
40719                                 }
40720                             }
40721                         }
40722                     }
40723                 }
40724
40725                 if (this.authenticated()) {
40726                     return oauth.xhr({ method: 'GET', path: path }, done);
40727                 } else {
40728                     var url = urlroot + path;
40729                     var controller = new AbortController();
40730                     d3_json(url, { signal: controller.signal })
40731                         .then(function(data) {
40732                             done(null, data);
40733                         })
40734                         .catch(function(err) {
40735                             if (err.name === 'AbortError') { return; }
40736                             // d3-fetch includes status in the error message,
40737                             // but we can't access the response itself
40738                             // https://github.com/d3/d3-fetch/issues/27
40739                             var match = err.message.match(/^\d{3}/);
40740                             if (match) {
40741                                 done({ status: +match[0], statusText: err.message });
40742                             } else {
40743                                 done(err.message);
40744                             }
40745                         });
40746                     return controller;
40747                 }
40748             },
40749
40750
40751             // Load a single entity by id (ways and relations use the `/full` call)
40752             // GET /api/0.6/node/#id
40753             // GET /api/0.6/[way|relation]/#id/full
40754             loadEntity: function(id, callback) {
40755                 var type = osmEntity.id.type(id);
40756                 var osmID = osmEntity.id.toOSM(id);
40757                 var options = { skipSeen: false };
40758
40759                 this.loadFromAPI(
40760                     '/api/0.6/' + type + '/' + osmID + (type !== 'node' ? '/full' : '') + '.json',
40761                     function(err, entities) {
40762                         if (callback) { callback(err, { data: entities }); }
40763                     },
40764                     options
40765                 );
40766             },
40767
40768
40769             // Load a single entity with a specific version
40770             // GET /api/0.6/[node|way|relation]/#id/#version
40771             loadEntityVersion: function(id, version, callback) {
40772                 var type = osmEntity.id.type(id);
40773                 var osmID = osmEntity.id.toOSM(id);
40774                 var options = { skipSeen: false };
40775
40776                 this.loadFromAPI(
40777                     '/api/0.6/' + type + '/' + osmID + '/' + version + '.json',
40778                     function(err, entities) {
40779                         if (callback) { callback(err, { data: entities }); }
40780                     },
40781                     options
40782                 );
40783             },
40784
40785
40786             // Load multiple entities in chunks
40787             // (note: callback may be called multiple times)
40788             // Unlike `loadEntity`, child nodes and members are not fetched
40789             // GET /api/0.6/[nodes|ways|relations]?#parameters
40790             loadMultiple: function(ids, callback) {
40791                 var that = this;
40792                 var groups = utilArrayGroupBy(utilArrayUniq(ids), osmEntity.id.type);
40793
40794                 Object.keys(groups).forEach(function(k) {
40795                     var type = k + 's';   // nodes, ways, relations
40796                     var osmIDs = groups[k].map(function(id) { return osmEntity.id.toOSM(id); });
40797                     var options = { skipSeen: false };
40798
40799                     utilArrayChunk(osmIDs, 150).forEach(function(arr) {
40800                         that.loadFromAPI(
40801                             '/api/0.6/' + type + '.json?' + type + '=' + arr.join(),
40802                             function(err, entities) {
40803                                 if (callback) { callback(err, { data: entities }); }
40804                             },
40805                             options
40806                         );
40807                     });
40808                 });
40809             },
40810
40811
40812             // Create, upload, and close a changeset
40813             // PUT /api/0.6/changeset/create
40814             // POST /api/0.6/changeset/#id/upload
40815             // PUT /api/0.6/changeset/#id/close
40816             putChangeset: function(changeset, changes, callback) {
40817                 var cid = _connectionID;
40818
40819                 if (_changeset.inflight) {
40820                     return callback({ message: 'Changeset already inflight', status: -2 }, changeset);
40821
40822                 } else if (_changeset.open) {   // reuse existing open changeset..
40823                     return createdChangeset.call(this, null, _changeset.open);
40824
40825                 } else {   // Open a new changeset..
40826                     var options = {
40827                         method: 'PUT',
40828                         path: '/api/0.6/changeset/create',
40829                         options: { header: { 'Content-Type': 'text/xml' } },
40830                         content: JXON.stringify(changeset.asJXON())
40831                     };
40832                     _changeset.inflight = oauth.xhr(
40833                         options,
40834                         wrapcb(this, createdChangeset, cid)
40835                     );
40836                 }
40837
40838
40839                 function createdChangeset(err, changesetID) {
40840                     _changeset.inflight = null;
40841                     if (err) { return callback(err, changeset); }
40842
40843                     _changeset.open = changesetID;
40844                     changeset = changeset.update({ id: changesetID });
40845
40846                     // Upload the changeset..
40847                     var options = {
40848                         method: 'POST',
40849                         path: '/api/0.6/changeset/' + changesetID + '/upload',
40850                         options: { header: { 'Content-Type': 'text/xml' } },
40851                         content: JXON.stringify(changeset.osmChangeJXON(changes))
40852                     };
40853                     _changeset.inflight = oauth.xhr(
40854                         options,
40855                         wrapcb(this, uploadedChangeset, cid)
40856                     );
40857                 }
40858
40859
40860                 function uploadedChangeset(err) {
40861                     _changeset.inflight = null;
40862                     if (err) { return callback(err, changeset); }
40863
40864                     // Upload was successful, safe to call the callback.
40865                     // Add delay to allow for postgres replication #1646 #2678
40866                     window.setTimeout(function() { callback(null, changeset); }, 2500);
40867                     _changeset.open = null;
40868
40869                     // At this point, we don't really care if the connection was switched..
40870                     // Only try to close the changeset if we're still talking to the same server.
40871                     if (this.getConnectionId() === cid) {
40872                         // Still attempt to close changeset, but ignore response because #2667
40873                         oauth.xhr({
40874                             method: 'PUT',
40875                             path: '/api/0.6/changeset/' + changeset.id + '/close',
40876                             options: { header: { 'Content-Type': 'text/xml' } }
40877                         }, function() { return true; });
40878                     }
40879                 }
40880             },
40881
40882
40883             // Load multiple users in chunks
40884             // (note: callback may be called multiple times)
40885             // GET /api/0.6/users?users=#id1,#id2,...,#idn
40886             loadUsers: function(uids, callback) {
40887                 var toLoad = [];
40888                 var cached = [];
40889
40890                 utilArrayUniq(uids).forEach(function(uid) {
40891                     if (_userCache.user[uid]) {
40892                         delete _userCache.toLoad[uid];
40893                         cached.push(_userCache.user[uid]);
40894                     } else {
40895                         toLoad.push(uid);
40896                     }
40897                 });
40898
40899                 if (cached.length || !this.authenticated()) {
40900                     callback(undefined, cached);
40901                     if (!this.authenticated()) { return; }  // require auth
40902                 }
40903
40904                 utilArrayChunk(toLoad, 150).forEach(function(arr) {
40905                     oauth.xhr(
40906                         { method: 'GET', path: '/api/0.6/users?users=' + arr.join() },
40907                         wrapcb(this, done, _connectionID)
40908                     );
40909                 }.bind(this));
40910
40911                 function done(err, xml) {
40912                     if (err) { return callback(err); }
40913
40914                     var options = { skipSeen: true };
40915                     return parseXML(xml, function(err, results) {
40916                         if (err) {
40917                             return callback(err);
40918                         } else {
40919                             return callback(undefined, results);
40920                         }
40921                     }, options);
40922                 }
40923             },
40924
40925
40926             // Load a given user by id
40927             // GET /api/0.6/user/#id
40928             loadUser: function(uid, callback) {
40929                 if (_userCache.user[uid] || !this.authenticated()) {   // require auth
40930                     delete _userCache.toLoad[uid];
40931                     return callback(undefined, _userCache.user[uid]);
40932                 }
40933
40934                 oauth.xhr(
40935                     { method: 'GET', path: '/api/0.6/user/' + uid },
40936                     wrapcb(this, done, _connectionID)
40937                 );
40938
40939                 function done(err, xml) {
40940                     if (err) { return callback(err); }
40941
40942                     var options = { skipSeen: true };
40943                     return parseXML(xml, function(err, results) {
40944                         if (err) {
40945                             return callback(err);
40946                         } else {
40947                             return callback(undefined, results[0]);
40948                         }
40949                     }, options);
40950                 }
40951             },
40952
40953
40954             // Load the details of the logged-in user
40955             // GET /api/0.6/user/details
40956             userDetails: function(callback) {
40957                 if (_userDetails) {    // retrieve cached
40958                     return callback(undefined, _userDetails);
40959                 }
40960
40961                 oauth.xhr(
40962                     { method: 'GET', path: '/api/0.6/user/details' },
40963                     wrapcb(this, done, _connectionID)
40964                 );
40965
40966                 function done(err, xml) {
40967                     if (err) { return callback(err); }
40968
40969                     var options = { skipSeen: false };
40970                     return parseXML(xml, function(err, results) {
40971                         if (err) {
40972                             return callback(err);
40973                         } else {
40974                             _userDetails = results[0];
40975                             return callback(undefined, _userDetails);
40976                         }
40977                     }, options);
40978                 }
40979             },
40980
40981
40982             // Load previous changesets for the logged in user
40983             // GET /api/0.6/changesets?user=#id
40984             userChangesets: function(callback) {
40985                 if (_userChangesets) {    // retrieve cached
40986                     return callback(undefined, _userChangesets);
40987                 }
40988
40989                 this.userDetails(
40990                     wrapcb(this, gotDetails, _connectionID)
40991                 );
40992
40993
40994                 function gotDetails(err, user) {
40995                     if (err) { return callback(err); }
40996
40997                     oauth.xhr(
40998                         { method: 'GET', path: '/api/0.6/changesets?user=' + user.id },
40999                         wrapcb(this, done, _connectionID)
41000                     );
41001                 }
41002
41003                 function done(err, xml) {
41004                     if (err) { return callback(err); }
41005
41006                     _userChangesets = Array.prototype.map.call(
41007                         xml.getElementsByTagName('changeset'),
41008                         function (changeset) { return { tags: getTags(changeset) }; }
41009                     ).filter(function (changeset) {
41010                         var comment = changeset.tags.comment;
41011                         return comment && comment !== '';
41012                     });
41013
41014                     return callback(undefined, _userChangesets);
41015                 }
41016             },
41017
41018
41019             // Fetch the status of the OSM API
41020             // GET /api/capabilities
41021             status: function(callback) {
41022                 var url = urlroot + '/api/capabilities';
41023                 var errback = wrapcb(this, done, _connectionID);
41024                 d3_xml(url)
41025                     .then(function(data) { errback(null, data); })
41026                     .catch(function(err) { errback(err.message); });
41027
41028                 function done(err, xml) {
41029                     if (err) {
41030                         // the status is null if no response could be retrieved
41031                         return callback(err, null);
41032                     }
41033
41034                     // update blacklists
41035                     var elements = xml.getElementsByTagName('blacklist');
41036                     var regexes = [];
41037                     for (var i = 0; i < elements.length; i++) {
41038                         var regex = elements[i].getAttribute('regex');  // needs unencode?
41039                         if (regex) {
41040                             regexes.push(regex);
41041                         }
41042                     }
41043                     if (regexes.length) {
41044                         _blacklists = regexes;
41045                     }
41046
41047                     if (_rateLimitError) {
41048                         return callback(_rateLimitError, 'rateLimited');
41049                     } else {
41050                         var waynodes = xml.getElementsByTagName('waynodes');
41051                         var maxWayNodes = waynodes.length && parseInt(waynodes[0].getAttribute('maximum'), 10);
41052                         if (maxWayNodes && isFinite(maxWayNodes)) { _maxWayNodes = maxWayNodes; }
41053
41054                         var apiStatus = xml.getElementsByTagName('status');
41055                         var val = apiStatus[0].getAttribute('api');
41056                         return callback(undefined, val);
41057                     }
41058                 }
41059             },
41060
41061             // Calls `status` and dispatches an `apiStatusChange` event if the returned
41062             // status differs from the cached status.
41063             reloadApiStatus: function() {
41064                 // throttle to avoid unncessary API calls
41065                 if (!this.throttledReloadApiStatus) {
41066                     var that = this;
41067                     this.throttledReloadApiStatus = throttle(function() {
41068                         that.status(function(err, status) {
41069                             if (status !== _cachedApiStatus) {
41070                                 _cachedApiStatus = status;
41071                                 dispatch$6.call('apiStatusChange', that, err, status);
41072                             }
41073                         });
41074                     }, 500);
41075                 }
41076                 this.throttledReloadApiStatus();
41077             },
41078
41079
41080             // Returns the maximum number of nodes a single way can have
41081             maxWayNodes: function() {
41082                 return _maxWayNodes;
41083             },
41084
41085
41086             // Load data (entities) from the API in tiles
41087             // GET /api/0.6/map?bbox=
41088             loadTiles: function(projection, callback) {
41089                 if (_off) { return; }
41090
41091                 // determine the needed tiles to cover the view
41092                 var tiles = tiler$5.zoomExtent([_tileZoom$3, _tileZoom$3]).getTiles(projection);
41093
41094                 // abort inflight requests that are no longer needed
41095                 var hadRequests = hasInflightRequests(_tileCache);
41096                 abortUnwantedRequests$3(_tileCache, tiles);
41097                 if (hadRequests && !hasInflightRequests(_tileCache)) {
41098                     dispatch$6.call('loaded');    // stop the spinner
41099                 }
41100
41101                 // issue new requests..
41102                 tiles.forEach(function(tile) {
41103                     this.loadTile(tile, callback);
41104                 }, this);
41105             },
41106
41107
41108             // Load a single data tile
41109             // GET /api/0.6/map?bbox=
41110             loadTile: function(tile, callback) {
41111                 if (_off) { return; }
41112                 if (_tileCache.loaded[tile.id] || _tileCache.inflight[tile.id]) { return; }
41113
41114                 if (!hasInflightRequests(_tileCache)) {
41115                     dispatch$6.call('loading');   // start the spinner
41116                 }
41117
41118                 var path = '/api/0.6/map.json?bbox=';
41119                 var options = { skipSeen: true };
41120
41121                 _tileCache.inflight[tile.id] = this.loadFromAPI(
41122                     path + tile.extent.toParam(),
41123                     tileCallback,
41124                     options
41125                 );
41126
41127                 function tileCallback(err, parsed) {
41128                     delete _tileCache.inflight[tile.id];
41129                     if (!err) {
41130                         delete _tileCache.toLoad[tile.id];
41131                         _tileCache.loaded[tile.id] = true;
41132                         var bbox = tile.extent.bbox();
41133                         bbox.id = tile.id;
41134                         _tileCache.rtree.insert(bbox);
41135                     }
41136                     if (callback) {
41137                         callback(err, Object.assign({ data: parsed }, tile));
41138                     }
41139                     if (!hasInflightRequests(_tileCache)) {
41140                         dispatch$6.call('loaded');     // stop the spinner
41141                     }
41142                 }
41143             },
41144
41145
41146             isDataLoaded: function(loc) {
41147                 var bbox = { minX: loc[0], minY: loc[1], maxX: loc[0], maxY: loc[1] };
41148                 return _tileCache.rtree.collides(bbox);
41149             },
41150
41151
41152             // load the tile that covers the given `loc`
41153             loadTileAtLoc: function(loc, callback) {
41154                 // Back off if the toLoad queue is filling up.. re #6417
41155                 // (Currently `loadTileAtLoc` requests are considered low priority - used by operations to
41156                 // let users safely edit geometries which extend to unloaded tiles.  We can drop some.)
41157                 if (Object.keys(_tileCache.toLoad).length > 50) { return; }
41158
41159                 var k = geoZoomToScale(_tileZoom$3 + 1);
41160                 var offset = geoRawMercator().scale(k)(loc);
41161                 var projection = geoRawMercator().transform({ k: k, x: -offset[0], y: -offset[1] });
41162                 var tiles = tiler$5.zoomExtent([_tileZoom$3, _tileZoom$3]).getTiles(projection);
41163
41164                 tiles.forEach(function(tile) {
41165                     if (_tileCache.toLoad[tile.id] || _tileCache.loaded[tile.id] || _tileCache.inflight[tile.id]) { return; }
41166
41167                     _tileCache.toLoad[tile.id] = true;
41168                     this.loadTile(tile, callback);
41169                 }, this);
41170             },
41171
41172
41173             // Load notes from the API in tiles
41174             // GET /api/0.6/notes?bbox=
41175             loadNotes: function(projection, noteOptions) {
41176                 noteOptions = Object.assign({ limit: 10000, closed: 7 }, noteOptions);
41177                 if (_off) { return; }
41178
41179                 var that = this;
41180                 var path = '/api/0.6/notes?limit=' + noteOptions.limit + '&closed=' + noteOptions.closed + '&bbox=';
41181                 var throttleLoadUsers = throttle(function() {
41182                     var uids = Object.keys(_userCache.toLoad);
41183                     if (!uids.length) { return; }
41184                     that.loadUsers(uids, function() {});  // eagerly load user details
41185                 }, 750);
41186
41187                 // determine the needed tiles to cover the view
41188                 var tiles = tiler$5.zoomExtent([_noteZoom, _noteZoom]).getTiles(projection);
41189
41190                 // abort inflight requests that are no longer needed
41191                 abortUnwantedRequests$3(_noteCache, tiles);
41192
41193                 // issue new requests..
41194                 tiles.forEach(function(tile) {
41195                     if (_noteCache.loaded[tile.id] || _noteCache.inflight[tile.id]) { return; }
41196
41197                     var options = { skipSeen: false };
41198                     _noteCache.inflight[tile.id] = that.loadFromAPI(
41199                         path + tile.extent.toParam(),
41200                         function(err) {
41201                             delete _noteCache.inflight[tile.id];
41202                             if (!err) {
41203                                 _noteCache.loaded[tile.id] = true;
41204                             }
41205                             throttleLoadUsers();
41206                             dispatch$6.call('loadedNotes');
41207                         },
41208                         options
41209                     );
41210                 });
41211             },
41212
41213
41214             // Create a note
41215             // POST /api/0.6/notes?params
41216             postNoteCreate: function(note, callback) {
41217                 if (!this.authenticated()) {
41218                     return callback({ message: 'Not Authenticated', status: -3 }, note);
41219                 }
41220                 if (_noteCache.inflightPost[note.id]) {
41221                     return callback({ message: 'Note update already inflight', status: -2 }, note);
41222                 }
41223
41224                 if (!note.loc[0] || !note.loc[1] || !note.newComment) { return; } // location & description required
41225
41226                 var comment = note.newComment;
41227                 if (note.newCategory && note.newCategory !== 'None') { comment += ' #' + note.newCategory; }
41228
41229                 var path = '/api/0.6/notes?' + utilQsString({ lon: note.loc[0], lat: note.loc[1], text: comment });
41230
41231                 _noteCache.inflightPost[note.id] = oauth.xhr(
41232                     { method: 'POST', path: path },
41233                     wrapcb(this, done, _connectionID)
41234                 );
41235
41236
41237                 function done(err, xml) {
41238                     delete _noteCache.inflightPost[note.id];
41239                     if (err) { return callback(err); }
41240
41241                     // we get the updated note back, remove from caches and reparse..
41242                     this.removeNote(note);
41243
41244                     var options = { skipSeen: false };
41245                     return parseXML(xml, function(err, results) {
41246                         if (err) {
41247                             return callback(err);
41248                         } else {
41249                             return callback(undefined, results[0]);
41250                         }
41251                     }, options);
41252                 }
41253             },
41254
41255
41256             // Update a note
41257             // POST /api/0.6/notes/#id/comment?text=comment
41258             // POST /api/0.6/notes/#id/close?text=comment
41259             // POST /api/0.6/notes/#id/reopen?text=comment
41260             postNoteUpdate: function(note, newStatus, callback) {
41261                 if (!this.authenticated()) {
41262                     return callback({ message: 'Not Authenticated', status: -3 }, note);
41263                 }
41264                 if (_noteCache.inflightPost[note.id]) {
41265                     return callback({ message: 'Note update already inflight', status: -2 }, note);
41266                 }
41267
41268                 var action;
41269                 if (note.status !== 'closed' && newStatus === 'closed') {
41270                     action = 'close';
41271                 } else if (note.status !== 'open' && newStatus === 'open') {
41272                     action = 'reopen';
41273                 } else {
41274                     action = 'comment';
41275                     if (!note.newComment) { return; } // when commenting, comment required
41276                 }
41277
41278                 var path = '/api/0.6/notes/' + note.id + '/' + action;
41279                 if (note.newComment) {
41280                     path += '?' + utilQsString({ text: note.newComment });
41281                 }
41282
41283                 _noteCache.inflightPost[note.id] = oauth.xhr(
41284                     { method: 'POST', path: path },
41285                     wrapcb(this, done, _connectionID)
41286                 );
41287
41288
41289                 function done(err, xml) {
41290                     delete _noteCache.inflightPost[note.id];
41291                     if (err) { return callback(err); }
41292
41293                     // we get the updated note back, remove from caches and reparse..
41294                     this.removeNote(note);
41295
41296                     // update closed note cache - used to populate `closed:note` changeset tag
41297                     if (action === 'close') {
41298                         _noteCache.closed[note.id] = true;
41299                     } else if (action === 'reopen') {
41300                         delete _noteCache.closed[note.id];
41301                     }
41302
41303                     var options = { skipSeen: false };
41304                     return parseXML(xml, function(err, results) {
41305                         if (err) {
41306                             return callback(err);
41307                         } else {
41308                             return callback(undefined, results[0]);
41309                         }
41310                     }, options);
41311                 }
41312             },
41313
41314
41315             switch: function(options) {
41316                 urlroot = options.urlroot;
41317
41318                 oauth.options(Object.assign({
41319                     url: urlroot,
41320                     loading: authLoading,
41321                     done: authDone
41322                 }, options));
41323
41324                 this.reset();
41325                 this.userChangesets(function() {});  // eagerly load user details/changesets
41326                 dispatch$6.call('change');
41327                 return this;
41328             },
41329
41330
41331             toggle: function(val) {
41332                 _off = !val;
41333                 return this;
41334             },
41335
41336
41337             isChangesetInflight: function() {
41338                 return !!_changeset.inflight;
41339             },
41340
41341
41342             // get/set cached data
41343             // This is used to save/restore the state when entering/exiting the walkthrough
41344             // Also used for testing purposes.
41345             caches: function(obj) {
41346                 function cloneCache(source) {
41347                     var target = {};
41348                     Object.keys(source).forEach(function(k) {
41349                         if (k === 'rtree') {
41350                             target.rtree = new RBush().fromJSON(source.rtree.toJSON());  // clone rbush
41351                         } else if (k === 'note') {
41352                             target.note = {};
41353                             Object.keys(source.note).forEach(function(id) {
41354                                 target.note[id] = osmNote(source.note[id]);   // copy notes
41355                             });
41356                         } else {
41357                             target[k] = JSON.parse(JSON.stringify(source[k]));   // clone deep
41358                         }
41359                     });
41360                     return target;
41361                 }
41362
41363                 if (!arguments.length) {
41364                     return {
41365                         tile: cloneCache(_tileCache),
41366                         note: cloneCache(_noteCache),
41367                         user: cloneCache(_userCache)
41368                     };
41369                 }
41370
41371                 // access caches directly for testing (e.g., loading notes rtree)
41372                 if (obj === 'get') {
41373                     return {
41374                         tile: _tileCache,
41375                         note: _noteCache,
41376                         user: _userCache
41377                     };
41378                 }
41379
41380                 if (obj.tile) {
41381                     _tileCache = obj.tile;
41382                     _tileCache.inflight = {};
41383                 }
41384                 if (obj.note) {
41385                     _noteCache = obj.note;
41386                     _noteCache.inflight = {};
41387                     _noteCache.inflightPost = {};
41388                 }
41389                 if (obj.user) {
41390                     _userCache = obj.user;
41391                 }
41392
41393                 return this;
41394             },
41395
41396
41397             logout: function() {
41398                 _userChangesets = undefined;
41399                 _userDetails = undefined;
41400                 oauth.logout();
41401                 dispatch$6.call('change');
41402                 return this;
41403             },
41404
41405
41406             authenticated: function() {
41407                 return oauth.authenticated();
41408             },
41409
41410
41411             authenticate: function(callback) {
41412                 var that = this;
41413                 var cid = _connectionID;
41414                 _userChangesets = undefined;
41415                 _userDetails = undefined;
41416
41417                 function done(err, res) {
41418                     if (err) {
41419                         if (callback) { callback(err); }
41420                         return;
41421                     }
41422                     if (that.getConnectionId() !== cid) {
41423                         if (callback) { callback({ message: 'Connection Switched', status: -1 }); }
41424                         return;
41425                     }
41426                     _rateLimitError = undefined;
41427                     dispatch$6.call('change');
41428                     if (callback) { callback(err, res); }
41429                     that.userChangesets(function() {});  // eagerly load user details/changesets
41430                 }
41431
41432                 return oauth.authenticate(done);
41433             },
41434
41435
41436             imageryBlacklists: function() {
41437                 return _blacklists;
41438             },
41439
41440
41441             tileZoom: function(val) {
41442                 if (!arguments.length) { return _tileZoom$3; }
41443                 _tileZoom$3 = val;
41444                 return this;
41445             },
41446
41447
41448             // get all cached notes covering the viewport
41449             notes: function(projection) {
41450                 var viewport = projection.clipExtent();
41451                 var min = [viewport[0][0], viewport[1][1]];
41452                 var max = [viewport[1][0], viewport[0][1]];
41453                 var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
41454
41455                 return _noteCache.rtree.search(bbox)
41456                     .map(function(d) { return d.data; });
41457             },
41458
41459
41460             // get a single note from the cache
41461             getNote: function(id) {
41462                 return _noteCache.note[id];
41463             },
41464
41465
41466             // remove a single note from the cache
41467             removeNote: function(note) {
41468                 if (!(note instanceof osmNote) || !note.id) { return; }
41469
41470                 delete _noteCache.note[note.id];
41471                 updateRtree$3(encodeNoteRtree(note), false);  // false = remove
41472             },
41473
41474
41475             // replace a single note in the cache
41476             replaceNote: function(note) {
41477                 if (!(note instanceof osmNote) || !note.id) { return; }
41478
41479                 _noteCache.note[note.id] = note;
41480                 updateRtree$3(encodeNoteRtree(note), true);  // true = replace
41481                 return note;
41482             },
41483
41484
41485             // Get an array of note IDs closed during this session.
41486             // Used to populate `closed:note` changeset tag
41487             getClosedIDs: function() {
41488                 return Object.keys(_noteCache.closed).sort();
41489             }
41490
41491         };
41492
41493         var apibase$3 = 'https://wiki.openstreetmap.org/w/api.php';
41494         var _inflight$1 = {};
41495         var _wikibaseCache = {};
41496         var _localeIDs = { en: false };
41497
41498
41499         var debouncedRequest = debounce(request, 500, { leading: false });
41500
41501         function request(url, callback) {
41502             if (_inflight$1[url]) { return; }
41503             var controller = new AbortController();
41504             _inflight$1[url] = controller;
41505
41506             d3_json(url, { signal: controller.signal })
41507                 .then(function(result) {
41508                     delete _inflight$1[url];
41509                     if (callback) { callback(null, result); }
41510                 })
41511                 .catch(function(err) {
41512                     delete _inflight$1[url];
41513                     if (err.name === 'AbortError') { return; }
41514                     if (callback) { callback(err.message); }
41515                 });
41516         }
41517
41518
41519         /**
41520          * Get the best string value from the descriptions/labels result
41521          * Note that if mediawiki doesn't recognize language code, it will return all values.
41522          * In that case, fallback to use English.
41523          * @param values object - either descriptions or labels
41524          * @param langCode String
41525          * @returns localized string
41526          */
41527         function localizedToString(values, langCode) {
41528             if (values) {
41529                 values = values[langCode] || values.en;
41530             }
41531             return values ? values.value : '';
41532         }
41533
41534
41535         var serviceOsmWikibase = {
41536
41537             init: function() {
41538                 _inflight$1 = {};
41539                 _wikibaseCache = {};
41540                 _localeIDs = {};
41541             },
41542
41543
41544             reset: function() {
41545                 Object.values(_inflight$1).forEach(function(controller) { controller.abort(); });
41546                 _inflight$1 = {};
41547             },
41548
41549
41550             /**
41551              * Get the best value for the property, or undefined if not found
41552              * @param entity object from wikibase
41553              * @param property string e.g. 'P4' for image
41554              * @param langCode string e.g. 'fr' for French
41555              */
41556             claimToValue: function(entity, property, langCode) {
41557                 if (!entity.claims[property]) { return undefined; }
41558                 var locale = _localeIDs[langCode];
41559                 var preferredPick, localePick;
41560
41561                 entity.claims[property].forEach(function(stmt) {
41562                     // If exists, use value limited to the needed language (has a qualifier P26 = locale)
41563                     // Or if not found, use the first value with the "preferred" rank
41564                     if (!preferredPick && stmt.rank === 'preferred') {
41565                         preferredPick = stmt;
41566                     }
41567                     if (locale && stmt.qualifiers && stmt.qualifiers.P26 &&
41568                         stmt.qualifiers.P26[0].datavalue.value.id === locale
41569                     ) {
41570                         localePick = stmt;
41571                     }
41572                 });
41573
41574                 var result = localePick || preferredPick;
41575                 if (result) {
41576                     var datavalue = result.mainsnak.datavalue;
41577                     return datavalue.type === 'wikibase-entityid' ? datavalue.value.id : datavalue.value;
41578                 } else {
41579                     return undefined;
41580                 }
41581             },
41582
41583
41584             /**
41585              * Convert monolingual property into a key-value object (language -> value)
41586              * @param entity object from wikibase
41587              * @param property string e.g. 'P31' for monolingual wiki page title
41588              */
41589             monolingualClaimToValueObj: function(entity, property) {
41590                 if (!entity || !entity.claims[property]) { return undefined; }
41591
41592                 return entity.claims[property].reduce(function(acc, obj) {
41593                     var value = obj.mainsnak.datavalue.value;
41594                     acc[value.language] = value.text;
41595                     return acc;
41596                 }, {});
41597             },
41598
41599
41600             toSitelink: function(key, value) {
41601                 var result = value ? ('Tag:' + key + '=' + value) : 'Key:' + key;
41602                 return result.replace(/_/g, ' ').trim();
41603             },
41604
41605
41606             //
41607             // Pass params object of the form:
41608             // {
41609             //   key: 'string',
41610             //   value: 'string',
41611             //   rtype: 'string',
41612             //   langCode: 'string'
41613             // }
41614             //
41615             getEntity: function(params, callback) {
41616                 var doRequest = params.debounce ? debouncedRequest : request;
41617                 var that = this;
41618                 var titles = [];
41619                 var result = {};
41620                 var rtypeSitelink = params.rtype ? ('Relation:' + params.rtype).replace(/_/g, ' ').trim() : false;
41621                 var keySitelink = params.key ? this.toSitelink(params.key) : false;
41622                 var tagSitelink = (params.key && params.value) ? this.toSitelink(params.key, params.value) : false;
41623                 var localeSitelink;
41624
41625                 if (params.langCode && _localeIDs[params.langCode] === undefined) {
41626                     // If this is the first time we are asking about this locale,
41627                     // fetch corresponding entity (if it exists), and cache it.
41628                     // If there is no such entry, cache `false` value to avoid re-requesting it.
41629                     localeSitelink = ('Locale:' + params.langCode).replace(/_/g, ' ').trim();
41630                     titles.push(localeSitelink);
41631                 }
41632
41633                 if (rtypeSitelink) {
41634                     if (_wikibaseCache[rtypeSitelink]) {
41635                         result.rtype = _wikibaseCache[rtypeSitelink];
41636                     } else {
41637                         titles.push(rtypeSitelink);
41638                     }
41639                 }
41640
41641                 if (keySitelink) {
41642                     if (_wikibaseCache[keySitelink]) {
41643                         result.key = _wikibaseCache[keySitelink];
41644                     } else {
41645                         titles.push(keySitelink);
41646                     }
41647                 }
41648
41649                 if (tagSitelink) {
41650                     if (_wikibaseCache[tagSitelink]) {
41651                         result.tag = _wikibaseCache[tagSitelink];
41652                     } else {
41653                         titles.push(tagSitelink);
41654                     }
41655                 }
41656
41657                 if (!titles.length) {
41658                     // Nothing to do, we already had everything in the cache
41659                     return callback(null, result);
41660                 }
41661
41662                 // Requesting just the user language code
41663                 // If backend recognizes the code, it will perform proper fallbacks,
41664                 // and the result will contain the requested code. If not, all values are returned:
41665                 // {"zh-tw":{"value":"...","language":"zh-tw","source-language":"zh-hant"}
41666                 // {"pt-br":{"value":"...","language":"pt","for-language":"pt-br"}}
41667                 var obj = {
41668                     action: 'wbgetentities',
41669                     sites: 'wiki',
41670                     titles: titles.join('|'),
41671                     languages: params.langCode,
41672                     languagefallback: 1,
41673                     origin: '*',
41674                     format: 'json',
41675                     // There is an MW Wikibase API bug https://phabricator.wikimedia.org/T212069
41676                     // We shouldn't use v1 until it gets fixed, but should switch to it afterwards
41677                     // formatversion: 2,
41678                 };
41679
41680                 var url = apibase$3 + '?' + utilQsString(obj);
41681                 doRequest(url, function(err, d) {
41682                     if (err) {
41683                         callback(err);
41684                     } else if (!d.success || d.error) {
41685                         callback(d.error.messages.map(function(v) { return v.html['*']; }).join('<br>'));
41686                     } else {
41687                         var localeID = false;
41688                         Object.values(d.entities).forEach(function(res) {
41689                             if (res.missing !== '') {
41690                                 // Simplify access to the localized values
41691                                 res.description = localizedToString(res.descriptions, params.langCode);
41692                                 res.label = localizedToString(res.labels, params.langCode);
41693
41694                                 var title = res.sitelinks.wiki.title;
41695                                 if (title === rtypeSitelink) {
41696                                     _wikibaseCache[rtypeSitelink] = res;
41697                                     result.rtype = res;
41698                                 } else if (title === keySitelink) {
41699                                     _wikibaseCache[keySitelink] = res;
41700                                     result.key = res;
41701                                 } else if (title === tagSitelink) {
41702                                     _wikibaseCache[tagSitelink] = res;
41703                                     result.tag = res;
41704                                 } else if (title === localeSitelink) {
41705                                     localeID = res.id;
41706                                 } else {
41707                                     console.log('Unexpected title ' + title);  // eslint-disable-line no-console
41708                                 }
41709                             }
41710                         });
41711
41712                         if (localeSitelink) {
41713                             // If locale ID is not found, store false to prevent repeated queries
41714                             that.addLocale(params.langCode, localeID);
41715                         }
41716
41717                         callback(null, result);
41718                     }
41719                 });
41720             },
41721
41722
41723             //
41724             // Pass params object of the form:
41725             // {
41726             //   key: 'string',     // required
41727             //   value: 'string'    // optional
41728             // }
41729             //   -or-
41730             // {
41731             //   rtype: 'rtype'     // relation type  (e.g. 'multipolygon')
41732             // }
41733             //
41734             // Get an result object used to display tag documentation
41735             // {
41736             //   title:        'string',
41737             //   description:  'string',
41738             //   editURL:      'string',
41739             //   imageURL:     'string',
41740             //   wiki:         { title: 'string', text: 'string', url: 'string' }
41741             // }
41742             //
41743             getDocs: function(params, callback) {
41744                 var that = this;
41745                 var langCode = _mainLocalizer.localeCode().toLowerCase();
41746                 params.langCode = langCode;
41747
41748                 this.getEntity(params, function(err, data) {
41749                     if (err) {
41750                         callback(err);
41751                         return;
41752                     }
41753
41754                     var entity = data.rtype || data.tag || data.key;
41755                     if (!entity) {
41756                         callback('No entity');
41757                         return;
41758                     }
41759
41760                     // prepare result
41761                     var result = {
41762                         title: entity.title,
41763                         description: entity.description,
41764                         editURL: 'https://wiki.openstreetmap.org/wiki/' + entity.title
41765                     };
41766
41767                     // add image
41768                     if (entity.claims) {
41769                         var imageroot;
41770                         var image = that.claimToValue(entity, 'P4', langCode);
41771                         if (image) {
41772                             imageroot = 'https://commons.wikimedia.org/w/index.php';
41773                         } else {
41774                             image = that.claimToValue(entity, 'P28', langCode);
41775                             if (image) {
41776                                 imageroot = 'https://wiki.openstreetmap.org/w/index.php';
41777                             }
41778                         }
41779                         if (imageroot && image) {
41780                             result.imageURL = imageroot + '?' + utilQsString({
41781                                 title: 'Special:Redirect/file/' + image,
41782                                 width: 400
41783                             });
41784                         }
41785                     }
41786
41787                     // Try to get a wiki page from tag data item first, followed by the corresponding key data item.
41788                     // If neither tag nor key data item contain a wiki page in the needed language nor English,
41789                     // get the first found wiki page from either the tag or the key item.
41790                     var rtypeWiki = that.monolingualClaimToValueObj(data.rtype, 'P31');
41791                     var tagWiki = that.monolingualClaimToValueObj(data.tag, 'P31');
41792                     var keyWiki = that.monolingualClaimToValueObj(data.key, 'P31');
41793
41794                     // If exact language code does not exist, try to find the first part before the '-'
41795                     // BUG: in some cases, a more elaborate fallback logic might be needed
41796                     var langPrefix = langCode.split('-', 2)[0];
41797
41798                     // use the first acceptable wiki page
41799                     result.wiki =
41800                         getWikiInfo(rtypeWiki, langCode, 'inspector.wiki_reference') ||
41801                         getWikiInfo(rtypeWiki, langPrefix, 'inspector.wiki_reference') ||
41802                         getWikiInfo(rtypeWiki, 'en', 'inspector.wiki_en_reference') ||
41803                         getWikiInfo(tagWiki, langCode, 'inspector.wiki_reference') ||
41804                         getWikiInfo(tagWiki, langPrefix, 'inspector.wiki_reference') ||
41805                         getWikiInfo(tagWiki, 'en', 'inspector.wiki_en_reference') ||
41806                         getWikiInfo(keyWiki, langCode, 'inspector.wiki_reference') ||
41807                         getWikiInfo(keyWiki, langPrefix, 'inspector.wiki_reference') ||
41808                         getWikiInfo(keyWiki, 'en', 'inspector.wiki_en_reference');
41809
41810                     callback(null, result);
41811
41812
41813                     // Helper method to get wiki info if a given language exists
41814                     function getWikiInfo(wiki, langCode, tKey) {
41815                         if (wiki && wiki[langCode]) {
41816                             return {
41817                                 title: wiki[langCode],
41818                                 text: tKey,
41819                                 url: 'https://wiki.openstreetmap.org/wiki/' + wiki[langCode]
41820                             };
41821                         }
41822                     }
41823                 });
41824             },
41825
41826
41827             addLocale: function(langCode, qid) {
41828                 // Makes it easier to unit test
41829                 _localeIDs[langCode] = qid;
41830             },
41831
41832
41833             apibase: function(val) {
41834                 if (!arguments.length) { return apibase$3; }
41835                 apibase$3 = val;
41836                 return this;
41837             }
41838
41839         };
41840
41841         var jsonpCache = {};
41842         window.jsonpCache = jsonpCache;
41843
41844         function jsonpRequest(url, callback) {
41845             var request = {
41846                 abort: function() {}
41847             };
41848
41849             if (window.JSONP_FIX) {
41850                 if (window.JSONP_DELAY === 0) {
41851                     callback(window.JSONP_FIX);
41852                 } else {
41853                     var t = window.setTimeout(function() {
41854                         callback(window.JSONP_FIX);
41855                     }, window.JSONP_DELAY || 0);
41856
41857                     request.abort = function() { window.clearTimeout(t); };
41858                 }
41859
41860                 return request;
41861             }
41862
41863             function rand() {
41864                 var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
41865                 var c = '';
41866                 var i = -1;
41867                 while (++i < 15) { c += chars.charAt(Math.floor(Math.random() * 52)); }
41868                 return c;
41869             }
41870
41871             function create(url) {
41872                 var e = url.match(/callback=(\w+)/);
41873                 var c = e ? e[1] : rand();
41874
41875                 jsonpCache[c] = function(data) {
41876                     if (jsonpCache[c]) {
41877                         callback(data);
41878                     }
41879                     finalize();
41880                 };
41881
41882                 function finalize() {
41883                     delete jsonpCache[c];
41884                     script.remove();
41885                 }
41886
41887                 request.abort = finalize;
41888                 return 'jsonpCache.' + c;
41889             }
41890
41891             var cb = create(url);
41892
41893             var script = select('head')
41894                 .append('script')
41895                 .attr('type', 'text/javascript')
41896                 .attr('src', url.replace(/(\{|%7B)callback(\}|%7D)/, cb));
41897
41898             return request;
41899         }
41900
41901         var bubbleApi = 'https://dev.virtualearth.net/mapcontrol/HumanScaleServices/GetBubbles.ashx?';
41902         var streetsideImagesApi = 'https://t.ssl.ak.tiles.virtualearth.net/tiles/';
41903         var bubbleAppKey = 'AuftgJsO0Xs8Ts4M1xZUQJQXJNsvmh3IV8DkNieCiy3tCwCUMq76-WpkrBtNAuEm';
41904         var pannellumViewerCSS = 'pannellum-streetside/pannellum.css';
41905         var pannellumViewerJS = 'pannellum-streetside/pannellum.js';
41906         var maxResults$2 = 2000;
41907         var tileZoom$2 = 16.5;
41908         var tiler$6 = utilTiler().zoomExtent([tileZoom$2, tileZoom$2]).skipNullIsland(true);
41909         var dispatch$7 = dispatch('loadedBubbles', 'viewerChanged');
41910         var minHfov = 10;         // zoom in degrees:  20, 10, 5
41911         var maxHfov = 90;         // zoom out degrees
41912         var defaultHfov = 45;
41913
41914         var _hires = false;
41915         var _resolution = 512;    // higher numbers are slower - 512, 1024, 2048, 4096
41916         var _currScene = 0;
41917         var _ssCache;
41918         var _pannellumViewer;
41919         var _sceneOptions;
41920         var _dataUrlArray = [];
41921
41922
41923         /**
41924          * abortRequest().
41925          */
41926         function abortRequest$6(i) {
41927           i.abort();
41928         }
41929
41930
41931         /**
41932          * localeTimeStamp().
41933          */
41934         function localeTimestamp(s) {
41935           if (!s) { return null; }
41936           var options = { day: 'numeric', month: 'short', year: 'numeric' };
41937           var d = new Date(s);
41938           if (isNaN(d.getTime())) { return null; }
41939           return d.toLocaleString(_mainLocalizer.localeCode(), options);
41940         }
41941
41942
41943         /**
41944          * loadTiles() wraps the process of generating tiles and then fetching image points for each tile.
41945          */
41946         function loadTiles$2(which, url, projection, margin) {
41947           var tiles = tiler$6.margin(margin).getTiles(projection);
41948
41949           // abort inflight requests that are no longer needed
41950           var cache = _ssCache[which];
41951           Object.keys(cache.inflight).forEach(function (k) {
41952             var wanted = tiles.find(function (tile) { return k.indexOf(tile.id + ',') === 0; });
41953             if (!wanted) {
41954               abortRequest$6(cache.inflight[k]);
41955               delete cache.inflight[k];
41956             }
41957           });
41958
41959           tiles.forEach(function (tile) { return loadNextTilePage$2(which, url, tile); });
41960         }
41961
41962
41963         /**
41964          * loadNextTilePage() load data for the next tile page in line.
41965          */
41966         function loadNextTilePage$2(which, url, tile) {
41967           var cache = _ssCache[which];
41968           var nextPage = cache.nextPage[tile.id] || 0;
41969           var id = tile.id + ',' + String(nextPage);
41970           if (cache.loaded[id] || cache.inflight[id]) { return; }
41971
41972           cache.inflight[id] = getBubbles(url, tile, function (bubbles) {
41973             cache.loaded[id] = true;
41974             delete cache.inflight[id];
41975             if (!bubbles) { return; }
41976
41977             // [].shift() removes the first element, some statistics info, not a bubble point
41978             bubbles.shift();
41979
41980             var features = bubbles.map(function (bubble) {
41981               if (cache.points[bubble.id]) { return null; }  // skip duplicates
41982
41983               var loc = [bubble.lo, bubble.la];
41984               var d = {
41985                 loc: loc,
41986                 key: bubble.id,
41987                 ca: bubble.he,
41988                 captured_at: bubble.cd,
41989                 captured_by: 'microsoft',
41990                 // nbn: bubble.nbn,
41991                 // pbn: bubble.pbn,
41992                 // ad: bubble.ad,
41993                 // rn: bubble.rn,
41994                 pr: bubble.pr,  // previous
41995                 ne: bubble.ne,  // next
41996                 pano: true,
41997                 sequenceKey: null
41998               };
41999
42000               cache.points[bubble.id] = d;
42001
42002               // a sequence starts here
42003               if (bubble.pr === undefined) {
42004                 cache.leaders.push(bubble.id);
42005               }
42006
42007               return {
42008                 minX: loc[0], minY: loc[1], maxX: loc[0], maxY: loc[1], data: d
42009               };
42010
42011             }).filter(Boolean);
42012
42013             cache.rtree.load(features);
42014
42015             connectSequences();
42016
42017             if (which === 'bubbles') {
42018               dispatch$7.call('loadedBubbles');
42019             }
42020           });
42021         }
42022
42023
42024         // call this sometimes to connect the bubbles into sequences
42025         function connectSequences() {
42026           var cache = _ssCache.bubbles;
42027           var keepLeaders = [];
42028
42029           for (var i = 0; i < cache.leaders.length; i++) {
42030             var bubble = cache.points[cache.leaders[i]];
42031             var seen = {};
42032
42033             // try to make a sequence.. use the key of the leader bubble.
42034             var sequence = { key: bubble.key, bubbles: [] };
42035             var complete = false;
42036
42037             do {
42038               sequence.bubbles.push(bubble);
42039               seen[bubble.key] = true;
42040
42041               if (bubble.ne === undefined) {
42042                 complete = true;
42043               } else {
42044                 bubble = cache.points[bubble.ne];  // advance to next
42045               }
42046             } while (bubble && !seen[bubble.key] && !complete);
42047
42048
42049             if (complete) {
42050               _ssCache.sequences[sequence.key] = sequence;
42051
42052               // assign bubbles to the sequence
42053               for (var j = 0; j < sequence.bubbles.length; j++) {
42054                 sequence.bubbles[j].sequenceKey = sequence.key;
42055               }
42056
42057               // create a GeoJSON LineString
42058               sequence.geojson = {
42059                 type: 'LineString',
42060                 properties: { key: sequence.key },
42061                 coordinates: sequence.bubbles.map(function (d) { return d.loc; })
42062               };
42063
42064             } else {
42065               keepLeaders.push(cache.leaders[i]);
42066             }
42067           }
42068
42069           // couldn't complete these, save for later
42070           cache.leaders = keepLeaders;
42071         }
42072
42073
42074         /**
42075          * getBubbles() handles the request to the server for a tile extent of 'bubbles' (streetside image locations).
42076          */
42077         function getBubbles(url, tile, callback) {
42078           var rect = tile.extent.rectangle();
42079           var urlForRequest = url + utilQsString({
42080             n: rect[3],
42081             s: rect[1],
42082             e: rect[2],
42083             w: rect[0],
42084             c: maxResults$2,
42085             appkey: bubbleAppKey,
42086             jsCallback: '{callback}'
42087           });
42088
42089           return jsonpRequest(urlForRequest, function (data) {
42090             if (!data || data.error) {
42091               callback(null);
42092             } else {
42093               callback(data);
42094             }
42095           });
42096         }
42097
42098
42099         // partition viewport into higher zoom tiles
42100         function partitionViewport$2(projection) {
42101           var z = geoScaleToZoom(projection.scale());
42102           var z2 = (Math.ceil(z * 2) / 2) + 2.5;   // round to next 0.5 and add 2.5
42103           var tiler = utilTiler().zoomExtent([z2, z2]);
42104
42105           return tiler.getTiles(projection)
42106             .map(function (tile) { return tile.extent; });
42107         }
42108
42109
42110         // no more than `limit` results per partition.
42111         function searchLimited$2(limit, projection, rtree) {
42112           limit = limit || 5;
42113
42114           return partitionViewport$2(projection)
42115             .reduce(function (result, extent) {
42116               var found = rtree.search(extent.bbox())
42117                 .slice(0, limit)
42118                 .map(function (d) { return d.data; });
42119
42120               return (found.length ? result.concat(found) : result);
42121             }, []);
42122         }
42123
42124
42125         /**
42126          * loadImage()
42127          */
42128         function loadImage(imgInfo) {
42129           return new Promise(function (resolve) {
42130             var img = new Image();
42131             img.onload = function () {
42132               var canvas = document.getElementById('ideditor-canvas' + imgInfo.face);
42133               var ctx = canvas.getContext('2d');
42134               ctx.drawImage(img, imgInfo.x, imgInfo.y);
42135               resolve({ imgInfo: imgInfo, status: 'ok' });
42136             };
42137             img.onerror = function () {
42138               resolve({ data: imgInfo, status: 'error' });
42139             };
42140             img.setAttribute('crossorigin', '');
42141             img.src = imgInfo.url;
42142           });
42143         }
42144
42145
42146         /**
42147          * loadCanvas()
42148          */
42149         function loadCanvas(imageGroup) {
42150           return Promise.all(imageGroup.map(loadImage))
42151             .then(function (data) {
42152               var canvas = document.getElementById('ideditor-canvas' + data[0].imgInfo.face);
42153               var which = { '01': 0, '02': 1, '03': 2, '10': 3, '11': 4, '12': 5 };
42154               var face = data[0].imgInfo.face;
42155               _dataUrlArray[which[face]] = canvas.toDataURL('image/jpeg', 1.0);
42156               return { status: 'loadCanvas for face ' + data[0].imgInfo.face + 'ok'};
42157             });
42158         }
42159
42160
42161         /**
42162          * loadFaces()
42163          */
42164         function loadFaces(faceGroup) {
42165           return Promise.all(faceGroup.map(loadCanvas))
42166             .then(function () { return { status: 'loadFaces done' }; });
42167         }
42168
42169
42170         function setupCanvas(selection, reset) {
42171           if (reset) {
42172             selection.selectAll('#ideditor-stitcher-canvases')
42173               .remove();
42174           }
42175
42176           // Add the Streetside working canvases. These are used for 'stitching', or combining,
42177           // multiple images for each of the six faces, before passing to the Pannellum control as DataUrls
42178           selection.selectAll('#ideditor-stitcher-canvases')
42179             .data([0])
42180             .enter()
42181             .append('div')
42182             .attr('id', 'ideditor-stitcher-canvases')
42183             .attr('display', 'none')
42184             .selectAll('canvas')
42185             .data(['canvas01', 'canvas02', 'canvas03', 'canvas10', 'canvas11', 'canvas12'])
42186             .enter()
42187             .append('canvas')
42188             .attr('id', function (d) { return 'ideditor-' + d; })
42189             .attr('width', _resolution)
42190             .attr('height', _resolution);
42191         }
42192
42193
42194         function qkToXY(qk) {
42195           var x = 0;
42196           var y = 0;
42197           var scale = 256;
42198           for (var i = qk.length; i > 0; i--) {
42199             var key = qk[i-1];
42200             x += (+(key === '1' || key === '3')) * scale;
42201             y += (+(key === '2' || key === '3')) * scale;
42202             scale *= 2;
42203           }
42204           return [x, y];
42205         }
42206
42207
42208         function getQuadKeys() {
42209           var dim = _resolution / 256;
42210           var quadKeys;
42211
42212           if (dim === 16) {
42213             quadKeys = [
42214               '0000','0001','0010','0011','0100','0101','0110','0111',  '1000','1001','1010','1011','1100','1101','1110','1111',
42215               '0002','0003','0012','0013','0102','0103','0112','0113',  '1002','1003','1012','1013','1102','1103','1112','1113',
42216               '0020','0021','0030','0031','0120','0121','0130','0131',  '1020','1021','1030','1031','1120','1121','1130','1131',
42217               '0022','0023','0032','0033','0122','0123','0132','0133',  '1022','1023','1032','1033','1122','1123','1132','1133',
42218               '0200','0201','0210','0211','0300','0301','0310','0311',  '1200','1201','1210','1211','1300','1301','1310','1311',
42219               '0202','0203','0212','0213','0302','0303','0312','0313',  '1202','1203','1212','1213','1302','1303','1312','1313',
42220               '0220','0221','0230','0231','0320','0321','0330','0331',  '1220','1221','1230','1231','1320','1321','1330','1331',
42221               '0222','0223','0232','0233','0322','0323','0332','0333',  '1222','1223','1232','1233','1322','1323','1332','1333',
42222
42223               '2000','2001','2010','2011','2100','2101','2110','2111',  '3000','3001','3010','3011','3100','3101','3110','3111',
42224               '2002','2003','2012','2013','2102','2103','2112','2113',  '3002','3003','3012','3013','3102','3103','3112','3113',
42225               '2020','2021','2030','2031','2120','2121','2130','2131',  '3020','3021','3030','3031','3120','3121','3130','3131',
42226               '2022','2023','2032','2033','2122','2123','2132','2133',  '3022','3023','3032','3033','3122','3123','3132','3133',
42227               '2200','2201','2210','2211','2300','2301','2310','2311',  '3200','3201','3210','3211','3300','3301','3310','3311',
42228               '2202','2203','2212','2213','2302','2303','2312','2313',  '3202','3203','3212','3213','3302','3303','3312','3313',
42229               '2220','2221','2230','2231','2320','2321','2330','2331',  '3220','3221','3230','3231','3320','3321','3330','3331',
42230               '2222','2223','2232','2233','2322','2323','2332','2333',  '3222','3223','3232','3233','3322','3323','3332','3333'
42231             ];
42232
42233           } else if (dim === 8) {
42234             quadKeys = [
42235               '000','001','010','011',  '100','101','110','111',
42236               '002','003','012','013',  '102','103','112','113',
42237               '020','021','030','031',  '120','121','130','131',
42238               '022','023','032','033',  '122','123','132','133',
42239
42240               '200','201','210','211',  '300','301','310','311',
42241               '202','203','212','213',  '302','303','312','313',
42242               '220','221','230','231',  '320','321','330','331',
42243               '222','223','232','233',  '322','323','332','333'
42244             ];
42245
42246           } else if (dim === 4) {
42247             quadKeys = [
42248               '00','01',  '10','11',
42249               '02','03',  '12','13',
42250
42251               '20','21',  '30','31',
42252               '22','23',  '32','33'
42253             ];
42254
42255           } else {  // dim === 2
42256             quadKeys = [
42257               '0', '1',
42258               '2', '3'
42259             ];
42260           }
42261
42262           return quadKeys;
42263         }
42264
42265
42266
42267         var serviceStreetside = {
42268           /**
42269            * init() initialize streetside.
42270            */
42271           init: function() {
42272             if (!_ssCache) {
42273               this.reset();
42274             }
42275
42276             this.event = utilRebind(this, dispatch$7, 'on');
42277           },
42278
42279           /**
42280            * reset() reset the cache.
42281            */
42282           reset: function() {
42283             if (_ssCache) {
42284               Object.values(_ssCache.bubbles.inflight).forEach(abortRequest$6);
42285             }
42286
42287             _ssCache = {
42288               bubbles: { inflight: {}, loaded: {}, nextPage: {}, rtree: new RBush(), points: {}, leaders: [] },
42289               sequences: {}
42290             };
42291           },
42292
42293           /**
42294            * bubbles()
42295            */
42296           bubbles: function(projection) {
42297             var limit = 5;
42298             return searchLimited$2(limit, projection, _ssCache.bubbles.rtree);
42299           },
42300
42301
42302           sequences: function(projection) {
42303             var viewport = projection.clipExtent();
42304             var min = [viewport[0][0], viewport[1][1]];
42305             var max = [viewport[1][0], viewport[0][1]];
42306             var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
42307             var seen = {};
42308             var results = [];
42309
42310             // all sequences for bubbles in viewport
42311             _ssCache.bubbles.rtree.search(bbox)
42312               .forEach(function (d) {
42313                 var key = d.data.sequenceKey;
42314                 if (key && !seen[key]) {
42315                     seen[key] = true;
42316                     results.push(_ssCache.sequences[key].geojson);
42317                 }
42318               });
42319
42320             return results;
42321           },
42322
42323
42324           /**
42325            * loadBubbles()
42326            */
42327           loadBubbles: function(projection, margin) {
42328             // by default: request 2 nearby tiles so we can connect sequences.
42329             if (margin === undefined) { margin = 2; }
42330
42331             loadTiles$2('bubbles', bubbleApi, projection, margin);
42332           },
42333
42334
42335           viewer: function() {
42336             return _pannellumViewer;
42337           },
42338
42339
42340           initViewer: function () {
42341             if (!window.pannellum) { return; }
42342             if (_pannellumViewer) { return; }
42343
42344             var sceneID = ++_currScene + '';
42345             var options = {
42346               'default': { firstScene: sceneID },
42347               scenes: {}
42348             };
42349             options.scenes[sceneID] = _sceneOptions;
42350
42351             _pannellumViewer = window.pannellum.viewer('ideditor-viewer-streetside', options);
42352           },
42353
42354
42355           /**
42356            * loadViewer() create the streeside viewer.
42357            */
42358           loadViewer: function(context) {
42359             var that = this;
42360
42361             var pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
42362
42363             // create ms-wrapper, a photo wrapper class
42364             var wrap = context.container().select('.photoviewer').selectAll('.ms-wrapper')
42365               .data([0]);
42366
42367             // inject ms-wrapper into the photoviewer div
42368             // (used by all to house each custom photo viewer)
42369             var wrapEnter = wrap.enter()
42370               .append('div')
42371               .attr('class', 'photo-wrapper ms-wrapper')
42372               .classed('hide', true);
42373
42374             // inject div to support streetside viewer (pannellum) and attribution line
42375             wrapEnter
42376               .append('div')
42377               .attr('id', 'ideditor-viewer-streetside')
42378               .on(pointerPrefix + 'down.streetside', function () {
42379                 select(window)
42380                   .on(pointerPrefix + 'move.streetside', function () {
42381                     dispatch$7.call('viewerChanged');
42382                   }, true);
42383               })
42384               .on(pointerPrefix + 'up.streetside pointercancel.streetside', function () {
42385                 select(window)
42386                   .on(pointerPrefix + 'move.streetside', null);
42387
42388                 // continue dispatching events for a few seconds, in case viewer has inertia.
42389                 var t = timer(function (elapsed) {
42390                   dispatch$7.call('viewerChanged');
42391                   if (elapsed > 2000) {
42392                     t.stop();
42393                   }
42394                 });
42395               })
42396               .append('div')
42397               .attr('class', 'photo-attribution fillD');
42398
42399             var controlsEnter = wrapEnter
42400               .append('div')
42401               .attr('class', 'photo-controls-wrap')
42402               .append('div')
42403               .attr('class', 'photo-controls');
42404
42405             controlsEnter
42406               .append('button')
42407               .on('click.back', step(-1))
42408               .text('◄');
42409
42410             controlsEnter
42411               .append('button')
42412               .on('click.forward', step(1))
42413               .text('►');
42414
42415
42416             // create working canvas for stitching together images
42417             wrap = wrap
42418               .merge(wrapEnter)
42419               .call(setupCanvas, true);
42420
42421             // load streetside pannellum viewer css
42422             select('head').selectAll('#ideditor-streetside-viewercss')
42423               .data([0])
42424               .enter()
42425               .append('link')
42426               .attr('id', 'ideditor-streetside-viewercss')
42427               .attr('rel', 'stylesheet')
42428               .attr('href', context.asset(pannellumViewerCSS));
42429
42430             // load streetside pannellum viewer js
42431             select('head').selectAll('#ideditor-streetside-viewerjs')
42432               .data([0])
42433               .enter()
42434               .append('script')
42435               .attr('id', 'ideditor-streetside-viewerjs')
42436               .attr('src', context.asset(pannellumViewerJS));
42437
42438
42439             // Register viewer resize handler
42440             context.ui().photoviewer.on('resize.streetside', function () {
42441               if (_pannellumViewer) {
42442                 _pannellumViewer.resize();
42443               }
42444             });
42445
42446
42447             function step(stepBy) {
42448               return function () {
42449                 var viewer = context.container().select('.photoviewer');
42450                 var selected = viewer.empty() ? undefined : viewer.datum();
42451                 if (!selected) { return; }
42452
42453                 var nextID = (stepBy === 1 ? selected.ne : selected.pr);
42454                 var yaw = _pannellumViewer.getYaw();
42455                 var ca = selected.ca + yaw;
42456                 var origin = selected.loc;
42457
42458                 // construct a search trapezoid pointing out from current bubble
42459                 var meters = 35;
42460                 var p1 = [
42461                   origin[0] + geoMetersToLon(meters / 5, origin[1]),
42462                   origin[1]
42463                 ];
42464                 var p2 = [
42465                   origin[0] + geoMetersToLon(meters / 2, origin[1]),
42466                   origin[1] + geoMetersToLat(meters)
42467                 ];
42468                 var p3 = [
42469                   origin[0] - geoMetersToLon(meters / 2, origin[1]),
42470                   origin[1] + geoMetersToLat(meters)
42471                 ];
42472                 var p4 = [
42473                   origin[0] - geoMetersToLon(meters / 5, origin[1]),
42474                   origin[1]
42475                 ];
42476
42477                 var poly = [p1, p2, p3, p4, p1];
42478
42479                 // rotate it to face forward/backward
42480                 var angle = (stepBy === 1 ? ca : ca + 180) * (Math.PI / 180);
42481                 poly = geoRotate(poly, -angle, origin);
42482
42483                 var extent = poly.reduce(function (extent, point) {
42484                   return extent.extend(geoExtent(point));
42485                 }, geoExtent());
42486
42487                 // find nearest other bubble in the search polygon
42488                 var minDist = Infinity;
42489                 _ssCache.bubbles.rtree.search(extent.bbox())
42490                   .forEach(function (d) {
42491                     if (d.data.key === selected.key) { return; }
42492                     if (!geoPointInPolygon(d.data.loc, poly)) { return; }
42493
42494                     var dist = geoVecLength(d.data.loc, selected.loc);
42495                     var theta = selected.ca - d.data.ca;
42496                     var minTheta = Math.min(Math.abs(theta), 360 - Math.abs(theta));
42497                     if (minTheta > 20) {
42498                       dist += 5;  // penalize distance if camera angles don't match
42499                     }
42500
42501                     if (dist < minDist) {
42502                       nextID = d.data.key;
42503                       minDist = dist;
42504                     }
42505                   });
42506
42507                 var nextBubble = nextID && _ssCache.bubbles.points[nextID];
42508                 if (!nextBubble) { return; }
42509
42510                 context.map().centerEase(nextBubble.loc);
42511
42512                 that.selectImage(context, nextBubble)
42513                   .then(function (response) {
42514                     if (response.status === 'ok') {
42515                       _sceneOptions.yaw = yaw;
42516                       that.showViewer(context);
42517                     }
42518                   });
42519               };
42520             }
42521           },
42522
42523
42524           /**
42525            * showViewer()
42526            */
42527           showViewer: function(context, yaw) {
42528             if (!_sceneOptions) { return; }
42529
42530             if (yaw !== undefined) {
42531               _sceneOptions.yaw = yaw;
42532             }
42533
42534             if (!_pannellumViewer) {
42535               this.initViewer();
42536             } else {
42537               // make a new scene
42538               var sceneID = ++_currScene + '';
42539               _pannellumViewer
42540                 .addScene(sceneID, _sceneOptions)
42541                 .loadScene(sceneID);
42542
42543               // remove previous scene
42544               if (_currScene > 2) {
42545                 sceneID = (_currScene - 1) + '';
42546                 _pannellumViewer
42547                   .removeScene(sceneID);
42548               }
42549             }
42550
42551             var wrap = context.container().select('.photoviewer')
42552               .classed('hide', false);
42553
42554             var isHidden = wrap.selectAll('.photo-wrapper.ms-wrapper.hide').size();
42555
42556             if (isHidden) {
42557               wrap
42558                 .selectAll('.photo-wrapper:not(.ms-wrapper)')
42559                 .classed('hide', true);
42560
42561               wrap
42562                 .selectAll('.photo-wrapper.ms-wrapper')
42563                 .classed('hide', false);
42564             }
42565
42566             return this;
42567           },
42568
42569
42570           /**
42571            * hideViewer()
42572            */
42573           hideViewer: function (context) {
42574             var viewer = context.container().select('.photoviewer');
42575             if (!viewer.empty()) { viewer.datum(null); }
42576
42577             viewer
42578               .classed('hide', true)
42579               .selectAll('.photo-wrapper')
42580               .classed('hide', true);
42581
42582             context.container().selectAll('.viewfield-group, .sequence, .icon-sign')
42583               .classed('currentView', false);
42584
42585             return this.setStyles(context, null, true);
42586           },
42587
42588
42589           /**
42590            * selectImage().
42591            */
42592           selectImage: function (context, d) {
42593             var that = this;
42594             var viewer = context.container().select('.photoviewer');
42595             if (!viewer.empty()) { viewer.datum(d); }
42596
42597             this.setStyles(context, null, true);
42598
42599             var wrap = context.container().select('.photoviewer .ms-wrapper');
42600             var attribution = wrap.selectAll('.photo-attribution').html('');
42601
42602             wrap.selectAll('.pnlm-load-box')   // display "loading.."
42603               .style('display', 'block');
42604
42605             if (!d) {
42606               return Promise.resolve({ status: 'ok' });
42607             }
42608
42609             var line1 = attribution
42610               .append('div')
42611               .attr('class', 'attribution-row');
42612
42613             var hiresDomId = utilUniqueDomId('streetside-hires');
42614
42615             // Add hires checkbox
42616             var label = line1
42617               .append('label')
42618               .attr('for', hiresDomId)
42619               .attr('class', 'streetside-hires');
42620
42621             label
42622               .append('input')
42623               .attr('type', 'checkbox')
42624               .attr('id', hiresDomId)
42625               .property('checked', _hires)
42626               .on('click', function () {
42627                 event.stopPropagation();
42628
42629                 _hires = !_hires;
42630                 _resolution = _hires ? 1024 : 512;
42631                 wrap.call(setupCanvas, true);
42632
42633                 var viewstate = {
42634                   yaw: _pannellumViewer.getYaw(),
42635                   pitch: _pannellumViewer.getPitch(),
42636                   hfov: _pannellumViewer.getHfov()
42637                 };
42638
42639                 that.selectImage(context, d)
42640                   .then(function (response) {
42641                     if (response.status === 'ok') {
42642                       _sceneOptions = Object.assign(_sceneOptions, viewstate);
42643                       that.showViewer(context);
42644                     }
42645                   });
42646               });
42647
42648             label
42649               .append('span')
42650               .text(_t('streetside.hires'));
42651
42652
42653             var captureInfo = line1
42654               .append('div')
42655               .attr('class', 'attribution-capture-info');
42656
42657             // Add capture date
42658             if (d.captured_by) {
42659               var yyyy = (new Date()).getFullYear();
42660
42661               captureInfo
42662                 .append('a')
42663                 .attr('class', 'captured_by')
42664                 .attr('target', '_blank')
42665                 .attr('href', 'https://www.microsoft.com/en-us/maps/streetside')
42666                 .text('©' + yyyy + ' Microsoft');
42667
42668               captureInfo
42669                 .append('span')
42670                 .text('|');
42671             }
42672
42673             if (d.captured_at) {
42674               captureInfo
42675                 .append('span')
42676                 .attr('class', 'captured_at')
42677                 .text(localeTimestamp(d.captured_at));
42678             }
42679
42680             // Add image links
42681             var line2 = attribution
42682               .append('div')
42683               .attr('class', 'attribution-row');
42684
42685             line2
42686               .append('a')
42687               .attr('class', 'image-view-link')
42688               .attr('target', '_blank')
42689               .attr('href', 'https://www.bing.com/maps?cp=' + d.loc[1] + '~' + d.loc[0] +
42690                 '&lvl=17&dir=' + d.ca + '&style=x&v=2&sV=1')
42691               .text(_t('streetside.view_on_bing'));
42692
42693             line2
42694               .append('a')
42695               .attr('class', 'image-report-link')
42696               .attr('target', '_blank')
42697               .attr('href', 'https://www.bing.com/maps/privacyreport/streetsideprivacyreport?bubbleid=' +
42698                 encodeURIComponent(d.key) + '&focus=photo&lat=' + d.loc[1] + '&lng=' + d.loc[0] + '&z=17')
42699               .text(_t('streetside.report'));
42700
42701
42702             var bubbleIdQuadKey = d.key.toString(4);
42703             var paddingNeeded = 16 - bubbleIdQuadKey.length;
42704             for (var i = 0; i < paddingNeeded; i++) {
42705               bubbleIdQuadKey = '0' + bubbleIdQuadKey;
42706             }
42707             var imgUrlPrefix = streetsideImagesApi + 'hs' + bubbleIdQuadKey;
42708             var imgUrlSuffix = '.jpg?g=6338&n=z';
42709
42710             // Cubemap face code order matters here: front=01, right=02, back=03, left=10, up=11, down=12
42711             var faceKeys = ['01','02','03','10','11','12'];
42712
42713             // Map images to cube faces
42714             var quadKeys = getQuadKeys();
42715             var faces = faceKeys.map(function (faceKey) {
42716               return quadKeys.map(function (quadKey) {
42717                 var xy = qkToXY(quadKey);
42718                 return {
42719                   face: faceKey,
42720                   url: imgUrlPrefix + faceKey + quadKey + imgUrlSuffix,
42721                   x: xy[0],
42722                   y: xy[1]
42723                 };
42724               });
42725             });
42726
42727             return loadFaces(faces)
42728               .then(function () {
42729                 _sceneOptions = {
42730                   showFullscreenCtrl: false,
42731                   autoLoad: true,
42732                   compass: true,
42733                   northOffset: d.ca,
42734                   yaw: 0,
42735                   minHfov: minHfov,
42736                   maxHfov: maxHfov,
42737                   hfov: defaultHfov,
42738                   type: 'cubemap',
42739                   cubeMap: [
42740                     _dataUrlArray[0],
42741                     _dataUrlArray[1],
42742                     _dataUrlArray[2],
42743                     _dataUrlArray[3],
42744                     _dataUrlArray[4],
42745                     _dataUrlArray[5]
42746                   ]
42747                 };
42748                 return { status: 'ok' };
42749               });
42750           },
42751
42752
42753           getSequenceKeyForBubble: function(d) {
42754             return d && d.sequenceKey;
42755           },
42756
42757
42758           // Updates the currently highlighted sequence and selected bubble.
42759           // Reset is only necessary when interacting with the viewport because
42760           // this implicitly changes the currently selected bubble/sequence
42761           setStyles: function (context, hovered, reset) {
42762             if (reset) {  // reset all layers
42763               context.container().selectAll('.viewfield-group')
42764                 .classed('highlighted', false)
42765                 .classed('hovered', false)
42766                 .classed('currentView', false);
42767
42768               context.container().selectAll('.sequence')
42769                 .classed('highlighted', false)
42770                 .classed('currentView', false);
42771             }
42772
42773             var hoveredBubbleKey = hovered && hovered.key;
42774             var hoveredSequenceKey = this.getSequenceKeyForBubble(hovered);
42775             var hoveredSequence = hoveredSequenceKey && _ssCache.sequences[hoveredSequenceKey];
42776             var hoveredBubbleKeys =  (hoveredSequence && hoveredSequence.bubbles.map(function (d) { return d.key; })) || [];
42777
42778             var viewer = context.container().select('.photoviewer');
42779             var selected = viewer.empty() ? undefined : viewer.datum();
42780             var selectedBubbleKey = selected && selected.key;
42781             var selectedSequenceKey = this.getSequenceKeyForBubble(selected);
42782             var selectedSequence = selectedSequenceKey && _ssCache.sequences[selectedSequenceKey];
42783             var selectedBubbleKeys = (selectedSequence && selectedSequence.bubbles.map(function (d) { return d.key; })) || [];
42784
42785             // highlight sibling viewfields on either the selected or the hovered sequences
42786             var highlightedBubbleKeys = utilArrayUnion(hoveredBubbleKeys, selectedBubbleKeys);
42787
42788             context.container().selectAll('.layer-streetside-images .viewfield-group')
42789               .classed('highlighted', function (d) { return highlightedBubbleKeys.indexOf(d.key) !== -1; })
42790               .classed('hovered',     function (d) { return d.key === hoveredBubbleKey; })
42791               .classed('currentView', function (d) { return d.key === selectedBubbleKey; });
42792
42793             context.container().selectAll('.layer-streetside-images .sequence')
42794               .classed('highlighted', function (d) { return d.properties.key === hoveredSequenceKey; })
42795               .classed('currentView', function (d) { return d.properties.key === selectedSequenceKey; });
42796
42797             // update viewfields if needed
42798             context.container().selectAll('.viewfield-group .viewfield')
42799               .attr('d', viewfieldPath);
42800
42801             function viewfieldPath() {
42802               var d = this.parentNode.__data__;
42803               if (d.pano && d.key !== selectedBubbleKey) {
42804                 return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0';
42805               } else {
42806                 return 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z';
42807               }
42808             }
42809
42810             return this;
42811           },
42812
42813
42814           /**
42815            * cache().
42816            */
42817           cache: function () {
42818             return _ssCache;
42819           }
42820         };
42821
42822         var apibase$4 = 'https://taginfo.openstreetmap.org/api/4/';
42823         var _inflight$2 = {};
42824         var _popularKeys = {};
42825         var _taginfoCache = {};
42826
42827         var tag_sorts = {
42828             point: 'count_nodes',
42829             vertex: 'count_nodes',
42830             area: 'count_ways',
42831             line: 'count_ways'
42832         };
42833         var tag_sort_members = {
42834             point: 'count_node_members',
42835             vertex: 'count_node_members',
42836             area: 'count_way_members',
42837             line: 'count_way_members',
42838             relation: 'count_relation_members'
42839         };
42840         var tag_filters = {
42841             point: 'nodes',
42842             vertex: 'nodes',
42843             area: 'ways',
42844             line: 'ways'
42845         };
42846         var tag_members_fractions = {
42847             point: 'count_node_members_fraction',
42848             vertex: 'count_node_members_fraction',
42849             area: 'count_way_members_fraction',
42850             line: 'count_way_members_fraction',
42851             relation: 'count_relation_members_fraction'
42852         };
42853
42854
42855         function sets(params, n, o) {
42856             if (params.geometry && o[params.geometry]) {
42857                 params[n] = o[params.geometry];
42858             }
42859             return params;
42860         }
42861
42862
42863         function setFilter(params) {
42864             return sets(params, 'filter', tag_filters);
42865         }
42866
42867
42868         function setSort(params) {
42869             return sets(params, 'sortname', tag_sorts);
42870         }
42871
42872
42873         function setSortMembers(params) {
42874             return sets(params, 'sortname', tag_sort_members);
42875         }
42876
42877
42878         function clean(params) {
42879             return utilObjectOmit(params, ['geometry', 'debounce']);
42880         }
42881
42882
42883         function filterKeys(type) {
42884             var count_type = type ? 'count_' + type : 'count_all';
42885             return function(d) {
42886                 return parseFloat(d[count_type]) > 2500 || d.in_wiki;
42887             };
42888         }
42889
42890
42891         function filterMultikeys(prefix) {
42892             return function(d) {
42893                 // d.key begins with prefix, and d.key contains no additional ':'s
42894                 var re = new RegExp('^' + prefix + '(.*)$');
42895                 var matches = d.key.match(re) || [];
42896                 return (matches.length === 2 && matches[1].indexOf(':') === -1);
42897             };
42898         }
42899
42900
42901         function filterValues(allowUpperCase) {
42902             return function(d) {
42903                 if (d.value.match(/[;,]/) !== null) { return false; }  // exclude some punctuation
42904                 if (!allowUpperCase && d.value.match(/[A-Z*]/) !== null) { return false; }  // exclude uppercase letters
42905                 return parseFloat(d.fraction) > 0.0;
42906             };
42907         }
42908
42909
42910         function filterRoles(geometry) {
42911             return function(d) {
42912                 if (d.role === '') { return false; } // exclude empty role
42913                 if (d.role.match(/[A-Z*;,]/) !== null) { return false; }  // exclude uppercase letters and some punctuation
42914                 return parseFloat(d[tag_members_fractions[geometry]]) > 0.0;
42915             };
42916         }
42917
42918
42919         function valKey(d) {
42920             return {
42921                 value: d.key,
42922                 title: d.key
42923             };
42924         }
42925
42926
42927         function valKeyDescription(d) {
42928             var obj = {
42929                 value: d.value,
42930                 title: d.description || d.value
42931             };
42932             if (d.count) {
42933                 obj.count = d.count;
42934             }
42935             return obj;
42936         }
42937
42938
42939         function roleKey(d) {
42940             return {
42941                 value: d.role,
42942                 title: d.role
42943             };
42944         }
42945
42946
42947         // sort keys with ':' lower than keys without ':'
42948         function sortKeys(a, b) {
42949             return (a.key.indexOf(':') === -1 && b.key.indexOf(':') !== -1) ? -1
42950                 : (a.key.indexOf(':') !== -1 && b.key.indexOf(':') === -1) ? 1
42951                 : 0;
42952         }
42953
42954
42955         var debouncedRequest$1 = debounce(request$1, 300, { leading: false });
42956
42957         function request$1(url, params, exactMatch, callback, loaded) {
42958             if (_inflight$2[url]) { return; }
42959
42960             if (checkCache(url, params, exactMatch, callback)) { return; }
42961
42962             var controller = new AbortController();
42963             _inflight$2[url] = controller;
42964
42965             d3_json(url, { signal: controller.signal })
42966                 .then(function(result) {
42967                     delete _inflight$2[url];
42968                     if (loaded) { loaded(null, result); }
42969                 })
42970                 .catch(function(err) {
42971                     delete _inflight$2[url];
42972                     if (err.name === 'AbortError') { return; }
42973                     if (loaded) { loaded(err.message); }
42974                 });
42975         }
42976
42977
42978         function checkCache(url, params, exactMatch, callback) {
42979             var rp = params.rp || 25;
42980             var testQuery = params.query || '';
42981             var testUrl = url;
42982
42983             do {
42984                 var hit = _taginfoCache[testUrl];
42985
42986                 // exact match, or shorter match yielding fewer than max results (rp)
42987                 if (hit && (url === testUrl || hit.length < rp)) {
42988                     callback(null, hit);
42989                     return true;
42990                 }
42991
42992                 // don't try to shorten the query
42993                 if (exactMatch || !testQuery.length) { return false; }
42994
42995                 // do shorten the query to see if we already have a cached result
42996                 // that has returned fewer than max results (rp)
42997                 testQuery = testQuery.slice(0, -1);
42998                 testUrl = url.replace(/&query=(.*?)&/, '&query=' + testQuery + '&');
42999             } while (testQuery.length >= 0);
43000
43001             return false;
43002         }
43003
43004
43005         var serviceTaginfo = {
43006
43007             init: function() {
43008                 _inflight$2 = {};
43009                 _taginfoCache = {};
43010                 _popularKeys = {
43011                     // manually exclude some keys – #5377, #7485
43012                     postal_code: true,
43013                     full_name: true,
43014                     loc_name: true,
43015                     reg_name: true,
43016                     short_name: true,
43017                     sorting_name: true,
43018                     artist_name: true,
43019                     nat_name: true,
43020                     long_name: true,
43021                     'bridge:name': true
43022                 };
43023
43024                 // Fetch popular keys.  We'll exclude these from `values`
43025                 // lookups because they stress taginfo, and they aren't likely
43026                 // to yield meaningful autocomplete results.. see #3955
43027                 var params = {
43028                     rp: 100,
43029                     sortname: 'values_all',
43030                     sortorder: 'desc',
43031                     page: 1,
43032                     debounce: false,
43033                     lang: _mainLocalizer.languageCode()
43034                 };
43035                 this.keys(params, function(err, data) {
43036                     if (err) { return; }
43037                     data.forEach(function(d) {
43038                         if (d.value === 'opening_hours') { return; }  // exception
43039                         _popularKeys[d.value] = true;
43040                     });
43041                 });
43042             },
43043
43044
43045             reset: function() {
43046                 Object.values(_inflight$2).forEach(function(controller) { controller.abort(); });
43047                 _inflight$2 = {};
43048             },
43049
43050
43051             keys: function(params, callback) {
43052                 var doRequest = params.debounce ? debouncedRequest$1 : request$1;
43053                 params = clean(setSort(params));
43054                 params = Object.assign({
43055                     rp: 10,
43056                     sortname: 'count_all',
43057                     sortorder: 'desc',
43058                     page: 1,
43059                     lang: _mainLocalizer.languageCode()
43060                 }, params);
43061
43062                 var url = apibase$4 + 'keys/all?' + utilQsString(params);
43063                 doRequest(url, params, false, callback, function(err, d) {
43064                     if (err) {
43065                         callback(err);
43066                     } else {
43067                         var f = filterKeys(params.filter);
43068                         var result = d.data.filter(f).sort(sortKeys).map(valKey);
43069                         _taginfoCache[url] = result;
43070                         callback(null, result);
43071                     }
43072                 });
43073             },
43074
43075
43076             multikeys: function(params, callback) {
43077                 var doRequest = params.debounce ? debouncedRequest$1 : request$1;
43078                 params = clean(setSort(params));
43079                 params = Object.assign({
43080                     rp: 25,
43081                     sortname: 'count_all',
43082                     sortorder: 'desc',
43083                     page: 1,
43084                     lang: _mainLocalizer.languageCode()
43085                 }, params);
43086
43087                 var prefix = params.query;
43088                 var url = apibase$4 + 'keys/all?' + utilQsString(params);
43089                 doRequest(url, params, true, callback, function(err, d) {
43090                     if (err) {
43091                         callback(err);
43092                     } else {
43093                         var f = filterMultikeys(prefix);
43094                         var result = d.data.filter(f).map(valKey);
43095                         _taginfoCache[url] = result;
43096                         callback(null, result);
43097                     }
43098                 });
43099             },
43100
43101
43102             values: function(params, callback) {
43103                 // Exclude popular keys from values lookups.. see #3955
43104                 var key = params.key;
43105                 if (key && _popularKeys[key]) {
43106                     callback(null, []);
43107                     return;
43108                 }
43109
43110                 var doRequest = params.debounce ? debouncedRequest$1 : request$1;
43111                 params = clean(setSort(setFilter(params)));
43112                 params = Object.assign({
43113                     rp: 25,
43114                     sortname: 'count_all',
43115                     sortorder: 'desc',
43116                     page: 1,
43117                     lang: _mainLocalizer.languageCode()
43118                 }, params);
43119
43120                 var url = apibase$4 + 'key/values?' + utilQsString(params);
43121                 doRequest(url, params, false, callback, function(err, d) {
43122                     if (err) {
43123                         callback(err);
43124                     } else {
43125                         // In most cases we prefer taginfo value results with lowercase letters.
43126                         // A few OSM keys expect values to contain uppercase values (see #3377).
43127                         // This is not an exhaustive list (e.g. `name` also has uppercase values)
43128                         // but these are the fields where taginfo value lookup is most useful.
43129                         var re = /network|taxon|genus|species|brand|grape_variety|royal_cypher|listed_status|booth|rating|stars|:output|_hours|_times|_ref|manufacturer|country|target|brewery/;
43130                         var allowUpperCase = re.test(params.key);
43131                         var f = filterValues(allowUpperCase);
43132
43133                         var result = d.data.filter(f).map(valKeyDescription);
43134                         _taginfoCache[url] = result;
43135                         callback(null, result);
43136                     }
43137                 });
43138             },
43139
43140
43141             roles: function(params, callback) {
43142                 var doRequest = params.debounce ? debouncedRequest$1 : request$1;
43143                 var geometry = params.geometry;
43144                 params = clean(setSortMembers(params));
43145                 params = Object.assign({
43146                     rp: 25,
43147                     sortname: 'count_all_members',
43148                     sortorder: 'desc',
43149                     page: 1,
43150                     lang: _mainLocalizer.languageCode()
43151                 }, params);
43152
43153                 var url = apibase$4 + 'relation/roles?' + utilQsString(params);
43154                 doRequest(url, params, true, callback, function(err, d) {
43155                     if (err) {
43156                         callback(err);
43157                     } else {
43158                         var f = filterRoles(geometry);
43159                         var result = d.data.filter(f).map(roleKey);
43160                         _taginfoCache[url] = result;
43161                         callback(null, result);
43162                     }
43163                 });
43164             },
43165
43166
43167             docs: function(params, callback) {
43168                 var doRequest = params.debounce ? debouncedRequest$1 : request$1;
43169                 params = clean(setSort(params));
43170
43171                 var path = 'key/wiki_pages?';
43172                 if (params.value) {
43173                     path = 'tag/wiki_pages?';
43174                 } else if (params.rtype) {
43175                     path = 'relation/wiki_pages?';
43176                 }
43177
43178                 var url = apibase$4 + path + utilQsString(params);
43179                 doRequest(url, params, true, callback, function(err, d) {
43180                     if (err) {
43181                         callback(err);
43182                     } else {
43183                         _taginfoCache[url] = d.data;
43184                         callback(null, d.data);
43185                     }
43186                 });
43187             },
43188
43189
43190             apibase: function(_) {
43191                 if (!arguments.length) { return apibase$4; }
43192                 apibase$4 = _;
43193                 return this;
43194             }
43195
43196         };
43197
43198         var helpers$1 = createCommonjsModule(function (module, exports) {
43199         Object.defineProperty(exports, "__esModule", { value: true });
43200         /**
43201          * @module helpers
43202          */
43203         /**
43204          * Earth Radius used with the Harvesine formula and approximates using a spherical (non-ellipsoid) Earth.
43205          *
43206          * @memberof helpers
43207          * @type {number}
43208          */
43209         exports.earthRadius = 6371008.8;
43210         /**
43211          * Unit of measurement factors using a spherical (non-ellipsoid) earth radius.
43212          *
43213          * @memberof helpers
43214          * @type {Object}
43215          */
43216         exports.factors = {
43217             centimeters: exports.earthRadius * 100,
43218             centimetres: exports.earthRadius * 100,
43219             degrees: exports.earthRadius / 111325,
43220             feet: exports.earthRadius * 3.28084,
43221             inches: exports.earthRadius * 39.370,
43222             kilometers: exports.earthRadius / 1000,
43223             kilometres: exports.earthRadius / 1000,
43224             meters: exports.earthRadius,
43225             metres: exports.earthRadius,
43226             miles: exports.earthRadius / 1609.344,
43227             millimeters: exports.earthRadius * 1000,
43228             millimetres: exports.earthRadius * 1000,
43229             nauticalmiles: exports.earthRadius / 1852,
43230             radians: 1,
43231             yards: exports.earthRadius / 1.0936,
43232         };
43233         /**
43234          * Units of measurement factors based on 1 meter.
43235          *
43236          * @memberof helpers
43237          * @type {Object}
43238          */
43239         exports.unitsFactors = {
43240             centimeters: 100,
43241             centimetres: 100,
43242             degrees: 1 / 111325,
43243             feet: 3.28084,
43244             inches: 39.370,
43245             kilometers: 1 / 1000,
43246             kilometres: 1 / 1000,
43247             meters: 1,
43248             metres: 1,
43249             miles: 1 / 1609.344,
43250             millimeters: 1000,
43251             millimetres: 1000,
43252             nauticalmiles: 1 / 1852,
43253             radians: 1 / exports.earthRadius,
43254             yards: 1 / 1.0936,
43255         };
43256         /**
43257          * Area of measurement factors based on 1 square meter.
43258          *
43259          * @memberof helpers
43260          * @type {Object}
43261          */
43262         exports.areaFactors = {
43263             acres: 0.000247105,
43264             centimeters: 10000,
43265             centimetres: 10000,
43266             feet: 10.763910417,
43267             inches: 1550.003100006,
43268             kilometers: 0.000001,
43269             kilometres: 0.000001,
43270             meters: 1,
43271             metres: 1,
43272             miles: 3.86e-7,
43273             millimeters: 1000000,
43274             millimetres: 1000000,
43275             yards: 1.195990046,
43276         };
43277         /**
43278          * Wraps a GeoJSON {@link Geometry} in a GeoJSON {@link Feature}.
43279          *
43280          * @name feature
43281          * @param {Geometry} geometry input geometry
43282          * @param {Object} [properties={}] an Object of key-value pairs to add as properties
43283          * @param {Object} [options={}] Optional Parameters
43284          * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
43285          * @param {string|number} [options.id] Identifier associated with the Feature
43286          * @returns {Feature} a GeoJSON Feature
43287          * @example
43288          * var geometry = {
43289          *   "type": "Point",
43290          *   "coordinates": [110, 50]
43291          * };
43292          *
43293          * var feature = turf.feature(geometry);
43294          *
43295          * //=feature
43296          */
43297         function feature(geom, properties, options) {
43298             if (options === void 0) { options = {}; }
43299             var feat = { type: "Feature" };
43300             if (options.id === 0 || options.id) {
43301                 feat.id = options.id;
43302             }
43303             if (options.bbox) {
43304                 feat.bbox = options.bbox;
43305             }
43306             feat.properties = properties || {};
43307             feat.geometry = geom;
43308             return feat;
43309         }
43310         exports.feature = feature;
43311         /**
43312          * Creates a GeoJSON {@link Geometry} from a Geometry string type & coordinates.
43313          * For GeometryCollection type use `helpers.geometryCollection`
43314          *
43315          * @name geometry
43316          * @param {string} type Geometry Type
43317          * @param {Array<any>} coordinates Coordinates
43318          * @param {Object} [options={}] Optional Parameters
43319          * @returns {Geometry} a GeoJSON Geometry
43320          * @example
43321          * var type = "Point";
43322          * var coordinates = [110, 50];
43323          * var geometry = turf.geometry(type, coordinates);
43324          * // => geometry
43325          */
43326         function geometry(type, coordinates, options) {
43327             switch (type) {
43328                 case "Point": return point(coordinates).geometry;
43329                 case "LineString": return lineString(coordinates).geometry;
43330                 case "Polygon": return polygon(coordinates).geometry;
43331                 case "MultiPoint": return multiPoint(coordinates).geometry;
43332                 case "MultiLineString": return multiLineString(coordinates).geometry;
43333                 case "MultiPolygon": return multiPolygon(coordinates).geometry;
43334                 default: throw new Error(type + " is invalid");
43335             }
43336         }
43337         exports.geometry = geometry;
43338         /**
43339          * Creates a {@link Point} {@link Feature} from a Position.
43340          *
43341          * @name point
43342          * @param {Array<number>} coordinates longitude, latitude position (each in decimal degrees)
43343          * @param {Object} [properties={}] an Object of key-value pairs to add as properties
43344          * @param {Object} [options={}] Optional Parameters
43345          * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
43346          * @param {string|number} [options.id] Identifier associated with the Feature
43347          * @returns {Feature<Point>} a Point feature
43348          * @example
43349          * var point = turf.point([-75.343, 39.984]);
43350          *
43351          * //=point
43352          */
43353         function point(coordinates, properties, options) {
43354             if (options === void 0) { options = {}; }
43355             var geom = {
43356                 type: "Point",
43357                 coordinates: coordinates,
43358             };
43359             return feature(geom, properties, options);
43360         }
43361         exports.point = point;
43362         /**
43363          * Creates a {@link Point} {@link FeatureCollection} from an Array of Point coordinates.
43364          *
43365          * @name points
43366          * @param {Array<Array<number>>} coordinates an array of Points
43367          * @param {Object} [properties={}] Translate these properties to each Feature
43368          * @param {Object} [options={}] Optional Parameters
43369          * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north]
43370          * associated with the FeatureCollection
43371          * @param {string|number} [options.id] Identifier associated with the FeatureCollection
43372          * @returns {FeatureCollection<Point>} Point Feature
43373          * @example
43374          * var points = turf.points([
43375          *   [-75, 39],
43376          *   [-80, 45],
43377          *   [-78, 50]
43378          * ]);
43379          *
43380          * //=points
43381          */
43382         function points(coordinates, properties, options) {
43383             if (options === void 0) { options = {}; }
43384             return featureCollection(coordinates.map(function (coords) {
43385                 return point(coords, properties);
43386             }), options);
43387         }
43388         exports.points = points;
43389         /**
43390          * Creates a {@link Polygon} {@link Feature} from an Array of LinearRings.
43391          *
43392          * @name polygon
43393          * @param {Array<Array<Array<number>>>} coordinates an array of LinearRings
43394          * @param {Object} [properties={}] an Object of key-value pairs to add as properties
43395          * @param {Object} [options={}] Optional Parameters
43396          * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
43397          * @param {string|number} [options.id] Identifier associated with the Feature
43398          * @returns {Feature<Polygon>} Polygon Feature
43399          * @example
43400          * var polygon = turf.polygon([[[-5, 52], [-4, 56], [-2, 51], [-7, 54], [-5, 52]]], { name: 'poly1' });
43401          *
43402          * //=polygon
43403          */
43404         function polygon(coordinates, properties, options) {
43405             if (options === void 0) { options = {}; }
43406             for (var _i = 0, coordinates_1 = coordinates; _i < coordinates_1.length; _i++) {
43407                 var ring = coordinates_1[_i];
43408                 if (ring.length < 4) {
43409                     throw new Error("Each LinearRing of a Polygon must have 4 or more Positions.");
43410                 }
43411                 for (var j = 0; j < ring[ring.length - 1].length; j++) {
43412                     // Check if first point of Polygon contains two numbers
43413                     if (ring[ring.length - 1][j] !== ring[0][j]) {
43414                         throw new Error("First and last Position are not equivalent.");
43415                     }
43416                 }
43417             }
43418             var geom = {
43419                 type: "Polygon",
43420                 coordinates: coordinates,
43421             };
43422             return feature(geom, properties, options);
43423         }
43424         exports.polygon = polygon;
43425         /**
43426          * Creates a {@link Polygon} {@link FeatureCollection} from an Array of Polygon coordinates.
43427          *
43428          * @name polygons
43429          * @param {Array<Array<Array<Array<number>>>>} coordinates an array of Polygon coordinates
43430          * @param {Object} [properties={}] an Object of key-value pairs to add as properties
43431          * @param {Object} [options={}] Optional Parameters
43432          * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
43433          * @param {string|number} [options.id] Identifier associated with the FeatureCollection
43434          * @returns {FeatureCollection<Polygon>} Polygon FeatureCollection
43435          * @example
43436          * var polygons = turf.polygons([
43437          *   [[[-5, 52], [-4, 56], [-2, 51], [-7, 54], [-5, 52]]],
43438          *   [[[-15, 42], [-14, 46], [-12, 41], [-17, 44], [-15, 42]]],
43439          * ]);
43440          *
43441          * //=polygons
43442          */
43443         function polygons(coordinates, properties, options) {
43444             if (options === void 0) { options = {}; }
43445             return featureCollection(coordinates.map(function (coords) {
43446                 return polygon(coords, properties);
43447             }), options);
43448         }
43449         exports.polygons = polygons;
43450         /**
43451          * Creates a {@link LineString} {@link Feature} from an Array of Positions.
43452          *
43453          * @name lineString
43454          * @param {Array<Array<number>>} coordinates an array of Positions
43455          * @param {Object} [properties={}] an Object of key-value pairs to add as properties
43456          * @param {Object} [options={}] Optional Parameters
43457          * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
43458          * @param {string|number} [options.id] Identifier associated with the Feature
43459          * @returns {Feature<LineString>} LineString Feature
43460          * @example
43461          * var linestring1 = turf.lineString([[-24, 63], [-23, 60], [-25, 65], [-20, 69]], {name: 'line 1'});
43462          * var linestring2 = turf.lineString([[-14, 43], [-13, 40], [-15, 45], [-10, 49]], {name: 'line 2'});
43463          *
43464          * //=linestring1
43465          * //=linestring2
43466          */
43467         function lineString(coordinates, properties, options) {
43468             if (options === void 0) { options = {}; }
43469             if (coordinates.length < 2) {
43470                 throw new Error("coordinates must be an array of two or more positions");
43471             }
43472             var geom = {
43473                 type: "LineString",
43474                 coordinates: coordinates,
43475             };
43476             return feature(geom, properties, options);
43477         }
43478         exports.lineString = lineString;
43479         /**
43480          * Creates a {@link LineString} {@link FeatureCollection} from an Array of LineString coordinates.
43481          *
43482          * @name lineStrings
43483          * @param {Array<Array<Array<number>>>} coordinates an array of LinearRings
43484          * @param {Object} [properties={}] an Object of key-value pairs to add as properties
43485          * @param {Object} [options={}] Optional Parameters
43486          * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north]
43487          * associated with the FeatureCollection
43488          * @param {string|number} [options.id] Identifier associated with the FeatureCollection
43489          * @returns {FeatureCollection<LineString>} LineString FeatureCollection
43490          * @example
43491          * var linestrings = turf.lineStrings([
43492          *   [[-24, 63], [-23, 60], [-25, 65], [-20, 69]],
43493          *   [[-14, 43], [-13, 40], [-15, 45], [-10, 49]]
43494          * ]);
43495          *
43496          * //=linestrings
43497          */
43498         function lineStrings(coordinates, properties, options) {
43499             if (options === void 0) { options = {}; }
43500             return featureCollection(coordinates.map(function (coords) {
43501                 return lineString(coords, properties);
43502             }), options);
43503         }
43504         exports.lineStrings = lineStrings;
43505         /**
43506          * Takes one or more {@link Feature|Features} and creates a {@link FeatureCollection}.
43507          *
43508          * @name featureCollection
43509          * @param {Feature[]} features input features
43510          * @param {Object} [options={}] Optional Parameters
43511          * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
43512          * @param {string|number} [options.id] Identifier associated with the Feature
43513          * @returns {FeatureCollection} FeatureCollection of Features
43514          * @example
43515          * var locationA = turf.point([-75.343, 39.984], {name: 'Location A'});
43516          * var locationB = turf.point([-75.833, 39.284], {name: 'Location B'});
43517          * var locationC = turf.point([-75.534, 39.123], {name: 'Location C'});
43518          *
43519          * var collection = turf.featureCollection([
43520          *   locationA,
43521          *   locationB,
43522          *   locationC
43523          * ]);
43524          *
43525          * //=collection
43526          */
43527         function featureCollection(features, options) {
43528             if (options === void 0) { options = {}; }
43529             var fc = { type: "FeatureCollection" };
43530             if (options.id) {
43531                 fc.id = options.id;
43532             }
43533             if (options.bbox) {
43534                 fc.bbox = options.bbox;
43535             }
43536             fc.features = features;
43537             return fc;
43538         }
43539         exports.featureCollection = featureCollection;
43540         /**
43541          * Creates a {@link Feature<MultiLineString>} based on a
43542          * coordinate array. Properties can be added optionally.
43543          *
43544          * @name multiLineString
43545          * @param {Array<Array<Array<number>>>} coordinates an array of LineStrings
43546          * @param {Object} [properties={}] an Object of key-value pairs to add as properties
43547          * @param {Object} [options={}] Optional Parameters
43548          * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
43549          * @param {string|number} [options.id] Identifier associated with the Feature
43550          * @returns {Feature<MultiLineString>} a MultiLineString feature
43551          * @throws {Error} if no coordinates are passed
43552          * @example
43553          * var multiLine = turf.multiLineString([[[0,0],[10,10]]]);
43554          *
43555          * //=multiLine
43556          */
43557         function multiLineString(coordinates, properties, options) {
43558             if (options === void 0) { options = {}; }
43559             var geom = {
43560                 type: "MultiLineString",
43561                 coordinates: coordinates,
43562             };
43563             return feature(geom, properties, options);
43564         }
43565         exports.multiLineString = multiLineString;
43566         /**
43567          * Creates a {@link Feature<MultiPoint>} based on a
43568          * coordinate array. Properties can be added optionally.
43569          *
43570          * @name multiPoint
43571          * @param {Array<Array<number>>} coordinates an array of Positions
43572          * @param {Object} [properties={}] an Object of key-value pairs to add as properties
43573          * @param {Object} [options={}] Optional Parameters
43574          * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
43575          * @param {string|number} [options.id] Identifier associated with the Feature
43576          * @returns {Feature<MultiPoint>} a MultiPoint feature
43577          * @throws {Error} if no coordinates are passed
43578          * @example
43579          * var multiPt = turf.multiPoint([[0,0],[10,10]]);
43580          *
43581          * //=multiPt
43582          */
43583         function multiPoint(coordinates, properties, options) {
43584             if (options === void 0) { options = {}; }
43585             var geom = {
43586                 type: "MultiPoint",
43587                 coordinates: coordinates,
43588             };
43589             return feature(geom, properties, options);
43590         }
43591         exports.multiPoint = multiPoint;
43592         /**
43593          * Creates a {@link Feature<MultiPolygon>} based on a
43594          * coordinate array. Properties can be added optionally.
43595          *
43596          * @name multiPolygon
43597          * @param {Array<Array<Array<Array<number>>>>} coordinates an array of Polygons
43598          * @param {Object} [properties={}] an Object of key-value pairs to add as properties
43599          * @param {Object} [options={}] Optional Parameters
43600          * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
43601          * @param {string|number} [options.id] Identifier associated with the Feature
43602          * @returns {Feature<MultiPolygon>} a multipolygon feature
43603          * @throws {Error} if no coordinates are passed
43604          * @example
43605          * var multiPoly = turf.multiPolygon([[[[0,0],[0,10],[10,10],[10,0],[0,0]]]]);
43606          *
43607          * //=multiPoly
43608          *
43609          */
43610         function multiPolygon(coordinates, properties, options) {
43611             if (options === void 0) { options = {}; }
43612             var geom = {
43613                 type: "MultiPolygon",
43614                 coordinates: coordinates,
43615             };
43616             return feature(geom, properties, options);
43617         }
43618         exports.multiPolygon = multiPolygon;
43619         /**
43620          * Creates a {@link Feature<GeometryCollection>} based on a
43621          * coordinate array. Properties can be added optionally.
43622          *
43623          * @name geometryCollection
43624          * @param {Array<Geometry>} geometries an array of GeoJSON Geometries
43625          * @param {Object} [properties={}] an Object of key-value pairs to add as properties
43626          * @param {Object} [options={}] Optional Parameters
43627          * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
43628          * @param {string|number} [options.id] Identifier associated with the Feature
43629          * @returns {Feature<GeometryCollection>} a GeoJSON GeometryCollection Feature
43630          * @example
43631          * var pt = turf.geometry("Point", [100, 0]);
43632          * var line = turf.geometry("LineString", [[101, 0], [102, 1]]);
43633          * var collection = turf.geometryCollection([pt, line]);
43634          *
43635          * // => collection
43636          */
43637         function geometryCollection(geometries, properties, options) {
43638             if (options === void 0) { options = {}; }
43639             var geom = {
43640                 type: "GeometryCollection",
43641                 geometries: geometries,
43642             };
43643             return feature(geom, properties, options);
43644         }
43645         exports.geometryCollection = geometryCollection;
43646         /**
43647          * Round number to precision
43648          *
43649          * @param {number} num Number
43650          * @param {number} [precision=0] Precision
43651          * @returns {number} rounded number
43652          * @example
43653          * turf.round(120.4321)
43654          * //=120
43655          *
43656          * turf.round(120.4321, 2)
43657          * //=120.43
43658          */
43659         function round(num, precision) {
43660             if (precision === void 0) { precision = 0; }
43661             if (precision && !(precision >= 0)) {
43662                 throw new Error("precision must be a positive number");
43663             }
43664             var multiplier = Math.pow(10, precision || 0);
43665             return Math.round(num * multiplier) / multiplier;
43666         }
43667         exports.round = round;
43668         /**
43669          * Convert a distance measurement (assuming a spherical Earth) from radians to a more friendly unit.
43670          * Valid units: miles, nauticalmiles, inches, yards, meters, metres, kilometers, centimeters, feet
43671          *
43672          * @name radiansToLength
43673          * @param {number} radians in radians across the sphere
43674          * @param {string} [units="kilometers"] can be degrees, radians, miles, or kilometers inches, yards, metres,
43675          * meters, kilometres, kilometers.
43676          * @returns {number} distance
43677          */
43678         function radiansToLength(radians, units) {
43679             if (units === void 0) { units = "kilometers"; }
43680             var factor = exports.factors[units];
43681             if (!factor) {
43682                 throw new Error(units + " units is invalid");
43683             }
43684             return radians * factor;
43685         }
43686         exports.radiansToLength = radiansToLength;
43687         /**
43688          * Convert a distance measurement (assuming a spherical Earth) from a real-world unit into radians
43689          * Valid units: miles, nauticalmiles, inches, yards, meters, metres, kilometers, centimeters, feet
43690          *
43691          * @name lengthToRadians
43692          * @param {number} distance in real units
43693          * @param {string} [units="kilometers"] can be degrees, radians, miles, or kilometers inches, yards, metres,
43694          * meters, kilometres, kilometers.
43695          * @returns {number} radians
43696          */
43697         function lengthToRadians(distance, units) {
43698             if (units === void 0) { units = "kilometers"; }
43699             var factor = exports.factors[units];
43700             if (!factor) {
43701                 throw new Error(units + " units is invalid");
43702             }
43703             return distance / factor;
43704         }
43705         exports.lengthToRadians = lengthToRadians;
43706         /**
43707          * Convert a distance measurement (assuming a spherical Earth) from a real-world unit into degrees
43708          * Valid units: miles, nauticalmiles, inches, yards, meters, metres, centimeters, kilometres, feet
43709          *
43710          * @name lengthToDegrees
43711          * @param {number} distance in real units
43712          * @param {string} [units="kilometers"] can be degrees, radians, miles, or kilometers inches, yards, metres,
43713          * meters, kilometres, kilometers.
43714          * @returns {number} degrees
43715          */
43716         function lengthToDegrees(distance, units) {
43717             return radiansToDegrees(lengthToRadians(distance, units));
43718         }
43719         exports.lengthToDegrees = lengthToDegrees;
43720         /**
43721          * Converts any bearing angle from the north line direction (positive clockwise)
43722          * and returns an angle between 0-360 degrees (positive clockwise), 0 being the north line
43723          *
43724          * @name bearingToAzimuth
43725          * @param {number} bearing angle, between -180 and +180 degrees
43726          * @returns {number} angle between 0 and 360 degrees
43727          */
43728         function bearingToAzimuth(bearing) {
43729             var angle = bearing % 360;
43730             if (angle < 0) {
43731                 angle += 360;
43732             }
43733             return angle;
43734         }
43735         exports.bearingToAzimuth = bearingToAzimuth;
43736         /**
43737          * Converts an angle in radians to degrees
43738          *
43739          * @name radiansToDegrees
43740          * @param {number} radians angle in radians
43741          * @returns {number} degrees between 0 and 360 degrees
43742          */
43743         function radiansToDegrees(radians) {
43744             var degrees = radians % (2 * Math.PI);
43745             return degrees * 180 / Math.PI;
43746         }
43747         exports.radiansToDegrees = radiansToDegrees;
43748         /**
43749          * Converts an angle in degrees to radians
43750          *
43751          * @name degreesToRadians
43752          * @param {number} degrees angle between 0 and 360 degrees
43753          * @returns {number} angle in radians
43754          */
43755         function degreesToRadians(degrees) {
43756             var radians = degrees % 360;
43757             return radians * Math.PI / 180;
43758         }
43759         exports.degreesToRadians = degreesToRadians;
43760         /**
43761          * Converts a length to the requested unit.
43762          * Valid units: miles, nauticalmiles, inches, yards, meters, metres, kilometers, centimeters, feet
43763          *
43764          * @param {number} length to be converted
43765          * @param {Units} [originalUnit="kilometers"] of the length
43766          * @param {Units} [finalUnit="kilometers"] returned unit
43767          * @returns {number} the converted length
43768          */
43769         function convertLength(length, originalUnit, finalUnit) {
43770             if (originalUnit === void 0) { originalUnit = "kilometers"; }
43771             if (finalUnit === void 0) { finalUnit = "kilometers"; }
43772             if (!(length >= 0)) {
43773                 throw new Error("length must be a positive number");
43774             }
43775             return radiansToLength(lengthToRadians(length, originalUnit), finalUnit);
43776         }
43777         exports.convertLength = convertLength;
43778         /**
43779          * Converts a area to the requested unit.
43780          * Valid units: kilometers, kilometres, meters, metres, centimetres, millimeters, acres, miles, yards, feet, inches
43781          * @param {number} area to be converted
43782          * @param {Units} [originalUnit="meters"] of the distance
43783          * @param {Units} [finalUnit="kilometers"] returned unit
43784          * @returns {number} the converted distance
43785          */
43786         function convertArea(area, originalUnit, finalUnit) {
43787             if (originalUnit === void 0) { originalUnit = "meters"; }
43788             if (finalUnit === void 0) { finalUnit = "kilometers"; }
43789             if (!(area >= 0)) {
43790                 throw new Error("area must be a positive number");
43791             }
43792             var startFactor = exports.areaFactors[originalUnit];
43793             if (!startFactor) {
43794                 throw new Error("invalid original units");
43795             }
43796             var finalFactor = exports.areaFactors[finalUnit];
43797             if (!finalFactor) {
43798                 throw new Error("invalid final units");
43799             }
43800             return (area / startFactor) * finalFactor;
43801         }
43802         exports.convertArea = convertArea;
43803         /**
43804          * isNumber
43805          *
43806          * @param {*} num Number to validate
43807          * @returns {boolean} true/false
43808          * @example
43809          * turf.isNumber(123)
43810          * //=true
43811          * turf.isNumber('foo')
43812          * //=false
43813          */
43814         function isNumber(num) {
43815             return !isNaN(num) && num !== null && !Array.isArray(num) && !/^\s*$/.test(num);
43816         }
43817         exports.isNumber = isNumber;
43818         /**
43819          * isObject
43820          *
43821          * @param {*} input variable to validate
43822          * @returns {boolean} true/false
43823          * @example
43824          * turf.isObject({elevation: 10})
43825          * //=true
43826          * turf.isObject('foo')
43827          * //=false
43828          */
43829         function isObject(input) {
43830             return (!!input) && (input.constructor === Object);
43831         }
43832         exports.isObject = isObject;
43833         /**
43834          * Validate BBox
43835          *
43836          * @private
43837          * @param {Array<number>} bbox BBox to validate
43838          * @returns {void}
43839          * @throws Error if BBox is not valid
43840          * @example
43841          * validateBBox([-180, -40, 110, 50])
43842          * //=OK
43843          * validateBBox([-180, -40])
43844          * //=Error
43845          * validateBBox('Foo')
43846          * //=Error
43847          * validateBBox(5)
43848          * //=Error
43849          * validateBBox(null)
43850          * //=Error
43851          * validateBBox(undefined)
43852          * //=Error
43853          */
43854         function validateBBox(bbox) {
43855             if (!bbox) {
43856                 throw new Error("bbox is required");
43857             }
43858             if (!Array.isArray(bbox)) {
43859                 throw new Error("bbox must be an Array");
43860             }
43861             if (bbox.length !== 4 && bbox.length !== 6) {
43862                 throw new Error("bbox must be an Array of 4 or 6 numbers");
43863             }
43864             bbox.forEach(function (num) {
43865                 if (!isNumber(num)) {
43866                     throw new Error("bbox must only contain numbers");
43867                 }
43868             });
43869         }
43870         exports.validateBBox = validateBBox;
43871         /**
43872          * Validate Id
43873          *
43874          * @private
43875          * @param {string|number} id Id to validate
43876          * @returns {void}
43877          * @throws Error if Id is not valid
43878          * @example
43879          * validateId([-180, -40, 110, 50])
43880          * //=Error
43881          * validateId([-180, -40])
43882          * //=Error
43883          * validateId('Foo')
43884          * //=OK
43885          * validateId(5)
43886          * //=OK
43887          * validateId(null)
43888          * //=Error
43889          * validateId(undefined)
43890          * //=Error
43891          */
43892         function validateId(id) {
43893             if (!id) {
43894                 throw new Error("id is required");
43895             }
43896             if (["string", "number"].indexOf(typeof id) === -1) {
43897                 throw new Error("id must be a number or a string");
43898             }
43899         }
43900         exports.validateId = validateId;
43901         // Deprecated methods
43902         function radians2degrees() {
43903             throw new Error("method has been renamed to `radiansToDegrees`");
43904         }
43905         exports.radians2degrees = radians2degrees;
43906         function degrees2radians() {
43907             throw new Error("method has been renamed to `degreesToRadians`");
43908         }
43909         exports.degrees2radians = degrees2radians;
43910         function distanceToDegrees() {
43911             throw new Error("method has been renamed to `lengthToDegrees`");
43912         }
43913         exports.distanceToDegrees = distanceToDegrees;
43914         function distanceToRadians() {
43915             throw new Error("method has been renamed to `lengthToRadians`");
43916         }
43917         exports.distanceToRadians = distanceToRadians;
43918         function radiansToDistance() {
43919             throw new Error("method has been renamed to `radiansToLength`");
43920         }
43921         exports.radiansToDistance = radiansToDistance;
43922         function bearingToAngle() {
43923             throw new Error("method has been renamed to `bearingToAzimuth`");
43924         }
43925         exports.bearingToAngle = bearingToAngle;
43926         function convertDistance() {
43927             throw new Error("method has been renamed to `convertLength`");
43928         }
43929         exports.convertDistance = convertDistance;
43930         });
43931
43932         var invariant = createCommonjsModule(function (module, exports) {
43933         Object.defineProperty(exports, "__esModule", { value: true });
43934
43935         /**
43936          * Unwrap a coordinate from a Point Feature, Geometry or a single coordinate.
43937          *
43938          * @name getCoord
43939          * @param {Array<number>|Geometry<Point>|Feature<Point>} coord GeoJSON Point or an Array of numbers
43940          * @returns {Array<number>} coordinates
43941          * @example
43942          * var pt = turf.point([10, 10]);
43943          *
43944          * var coord = turf.getCoord(pt);
43945          * //= [10, 10]
43946          */
43947         function getCoord(coord) {
43948             if (!coord) {
43949                 throw new Error("coord is required");
43950             }
43951             if (!Array.isArray(coord)) {
43952                 if (coord.type === "Feature" && coord.geometry !== null && coord.geometry.type === "Point") {
43953                     return coord.geometry.coordinates;
43954                 }
43955                 if (coord.type === "Point") {
43956                     return coord.coordinates;
43957                 }
43958             }
43959             if (Array.isArray(coord) && coord.length >= 2 && !Array.isArray(coord[0]) && !Array.isArray(coord[1])) {
43960                 return coord;
43961             }
43962             throw new Error("coord must be GeoJSON Point or an Array of numbers");
43963         }
43964         exports.getCoord = getCoord;
43965         /**
43966          * Unwrap coordinates from a Feature, Geometry Object or an Array
43967          *
43968          * @name getCoords
43969          * @param {Array<any>|Geometry|Feature} coords Feature, Geometry Object or an Array
43970          * @returns {Array<any>} coordinates
43971          * @example
43972          * var poly = turf.polygon([[[119.32, -8.7], [119.55, -8.69], [119.51, -8.54], [119.32, -8.7]]]);
43973          *
43974          * var coords = turf.getCoords(poly);
43975          * //= [[[119.32, -8.7], [119.55, -8.69], [119.51, -8.54], [119.32, -8.7]]]
43976          */
43977         function getCoords(coords) {
43978             if (Array.isArray(coords)) {
43979                 return coords;
43980             }
43981             // Feature
43982             if (coords.type === "Feature") {
43983                 if (coords.geometry !== null) {
43984                     return coords.geometry.coordinates;
43985                 }
43986             }
43987             else {
43988                 // Geometry
43989                 if (coords.coordinates) {
43990                     return coords.coordinates;
43991                 }
43992             }
43993             throw new Error("coords must be GeoJSON Feature, Geometry Object or an Array");
43994         }
43995         exports.getCoords = getCoords;
43996         /**
43997          * Checks if coordinates contains a number
43998          *
43999          * @name containsNumber
44000          * @param {Array<any>} coordinates GeoJSON Coordinates
44001          * @returns {boolean} true if Array contains a number
44002          */
44003         function containsNumber(coordinates) {
44004             if (coordinates.length > 1 && helpers$1.isNumber(coordinates[0]) && helpers$1.isNumber(coordinates[1])) {
44005                 return true;
44006             }
44007             if (Array.isArray(coordinates[0]) && coordinates[0].length) {
44008                 return containsNumber(coordinates[0]);
44009             }
44010             throw new Error("coordinates must only contain numbers");
44011         }
44012         exports.containsNumber = containsNumber;
44013         /**
44014          * Enforce expectations about types of GeoJSON objects for Turf.
44015          *
44016          * @name geojsonType
44017          * @param {GeoJSON} value any GeoJSON object
44018          * @param {string} type expected GeoJSON type
44019          * @param {string} name name of calling function
44020          * @throws {Error} if value is not the expected type.
44021          */
44022         function geojsonType(value, type, name) {
44023             if (!type || !name) {
44024                 throw new Error("type and name required");
44025             }
44026             if (!value || value.type !== type) {
44027                 throw new Error("Invalid input to " + name + ": must be a " + type + ", given " + value.type);
44028             }
44029         }
44030         exports.geojsonType = geojsonType;
44031         /**
44032          * Enforce expectations about types of {@link Feature} inputs for Turf.
44033          * Internally this uses {@link geojsonType} to judge geometry types.
44034          *
44035          * @name featureOf
44036          * @param {Feature} feature a feature with an expected geometry type
44037          * @param {string} type expected GeoJSON type
44038          * @param {string} name name of calling function
44039          * @throws {Error} error if value is not the expected type.
44040          */
44041         function featureOf(feature, type, name) {
44042             if (!feature) {
44043                 throw new Error("No feature passed");
44044             }
44045             if (!name) {
44046                 throw new Error(".featureOf() requires a name");
44047             }
44048             if (!feature || feature.type !== "Feature" || !feature.geometry) {
44049                 throw new Error("Invalid input to " + name + ", Feature with geometry required");
44050             }
44051             if (!feature.geometry || feature.geometry.type !== type) {
44052                 throw new Error("Invalid input to " + name + ": must be a " + type + ", given " + feature.geometry.type);
44053             }
44054         }
44055         exports.featureOf = featureOf;
44056         /**
44057          * Enforce expectations about types of {@link FeatureCollection} inputs for Turf.
44058          * Internally this uses {@link geojsonType} to judge geometry types.
44059          *
44060          * @name collectionOf
44061          * @param {FeatureCollection} featureCollection a FeatureCollection for which features will be judged
44062          * @param {string} type expected GeoJSON type
44063          * @param {string} name name of calling function
44064          * @throws {Error} if value is not the expected type.
44065          */
44066         function collectionOf(featureCollection, type, name) {
44067             if (!featureCollection) {
44068                 throw new Error("No featureCollection passed");
44069             }
44070             if (!name) {
44071                 throw new Error(".collectionOf() requires a name");
44072             }
44073             if (!featureCollection || featureCollection.type !== "FeatureCollection") {
44074                 throw new Error("Invalid input to " + name + ", FeatureCollection required");
44075             }
44076             for (var _i = 0, _a = featureCollection.features; _i < _a.length; _i++) {
44077                 var feature = _a[_i];
44078                 if (!feature || feature.type !== "Feature" || !feature.geometry) {
44079                     throw new Error("Invalid input to " + name + ", Feature with geometry required");
44080                 }
44081                 if (!feature.geometry || feature.geometry.type !== type) {
44082                     throw new Error("Invalid input to " + name + ": must be a " + type + ", given " + feature.geometry.type);
44083                 }
44084             }
44085         }
44086         exports.collectionOf = collectionOf;
44087         /**
44088          * Get Geometry from Feature or Geometry Object
44089          *
44090          * @param {Feature|Geometry} geojson GeoJSON Feature or Geometry Object
44091          * @returns {Geometry|null} GeoJSON Geometry Object
44092          * @throws {Error} if geojson is not a Feature or Geometry Object
44093          * @example
44094          * var point = {
44095          *   "type": "Feature",
44096          *   "properties": {},
44097          *   "geometry": {
44098          *     "type": "Point",
44099          *     "coordinates": [110, 40]
44100          *   }
44101          * }
44102          * var geom = turf.getGeom(point)
44103          * //={"type": "Point", "coordinates": [110, 40]}
44104          */
44105         function getGeom(geojson) {
44106             if (geojson.type === "Feature") {
44107                 return geojson.geometry;
44108             }
44109             return geojson;
44110         }
44111         exports.getGeom = getGeom;
44112         /**
44113          * Get GeoJSON object's type, Geometry type is prioritize.
44114          *
44115          * @param {GeoJSON} geojson GeoJSON object
44116          * @param {string} [name="geojson"] name of the variable to display in error message
44117          * @returns {string} GeoJSON type
44118          * @example
44119          * var point = {
44120          *   "type": "Feature",
44121          *   "properties": {},
44122          *   "geometry": {
44123          *     "type": "Point",
44124          *     "coordinates": [110, 40]
44125          *   }
44126          * }
44127          * var geom = turf.getType(point)
44128          * //="Point"
44129          */
44130         function getType(geojson, name) {
44131             if (geojson.type === "FeatureCollection") {
44132                 return "FeatureCollection";
44133             }
44134             if (geojson.type === "GeometryCollection") {
44135                 return "GeometryCollection";
44136             }
44137             if (geojson.type === "Feature" && geojson.geometry !== null) {
44138                 return geojson.geometry.type;
44139             }
44140             return geojson.type;
44141         }
44142         exports.getType = getType;
44143         });
44144
44145         var lineclip_1 = lineclip;
44146         var _default = lineclip;
44147
44148         lineclip.polyline = lineclip;
44149         lineclip.polygon = polygonclip;
44150
44151
44152         // Cohen-Sutherland line clippign algorithm, adapted to efficiently
44153         // handle polylines rather than just segments
44154
44155         function lineclip(points, bbox, result) {
44156
44157             var len = points.length,
44158                 codeA = bitCode(points[0], bbox),
44159                 part = [],
44160                 i, a, b, codeB, lastCode;
44161
44162             if (!result) { result = []; }
44163
44164             for (i = 1; i < len; i++) {
44165                 a = points[i - 1];
44166                 b = points[i];
44167                 codeB = lastCode = bitCode(b, bbox);
44168
44169                 while (true) {
44170
44171                     if (!(codeA | codeB)) { // accept
44172                         part.push(a);
44173
44174                         if (codeB !== lastCode) { // segment went outside
44175                             part.push(b);
44176
44177                             if (i < len - 1) { // start a new line
44178                                 result.push(part);
44179                                 part = [];
44180                             }
44181                         } else if (i === len - 1) {
44182                             part.push(b);
44183                         }
44184                         break;
44185
44186                     } else if (codeA & codeB) { // trivial reject
44187                         break;
44188
44189                     } else if (codeA) { // a outside, intersect with clip edge
44190                         a = intersect(a, b, codeA, bbox);
44191                         codeA = bitCode(a, bbox);
44192
44193                     } else { // b outside
44194                         b = intersect(a, b, codeB, bbox);
44195                         codeB = bitCode(b, bbox);
44196                     }
44197                 }
44198
44199                 codeA = lastCode;
44200             }
44201
44202             if (part.length) { result.push(part); }
44203
44204             return result;
44205         }
44206
44207         // Sutherland-Hodgeman polygon clipping algorithm
44208
44209         function polygonclip(points, bbox) {
44210
44211             var result, edge, prev, prevInside, i, p, inside;
44212
44213             // clip against each side of the clip rectangle
44214             for (edge = 1; edge <= 8; edge *= 2) {
44215                 result = [];
44216                 prev = points[points.length - 1];
44217                 prevInside = !(bitCode(prev, bbox) & edge);
44218
44219                 for (i = 0; i < points.length; i++) {
44220                     p = points[i];
44221                     inside = !(bitCode(p, bbox) & edge);
44222
44223                     // if segment goes through the clip window, add an intersection
44224                     if (inside !== prevInside) { result.push(intersect(prev, p, edge, bbox)); }
44225
44226                     if (inside) { result.push(p); } // add a point if it's inside
44227
44228                     prev = p;
44229                     prevInside = inside;
44230                 }
44231
44232                 points = result;
44233
44234                 if (!points.length) { break; }
44235             }
44236
44237             return result;
44238         }
44239
44240         // intersect a segment against one of the 4 lines that make up the bbox
44241
44242         function intersect(a, b, edge, bbox) {
44243             return edge & 8 ? [a[0] + (b[0] - a[0]) * (bbox[3] - a[1]) / (b[1] - a[1]), bbox[3]] : // top
44244                    edge & 4 ? [a[0] + (b[0] - a[0]) * (bbox[1] - a[1]) / (b[1] - a[1]), bbox[1]] : // bottom
44245                    edge & 2 ? [bbox[2], a[1] + (b[1] - a[1]) * (bbox[2] - a[0]) / (b[0] - a[0])] : // right
44246                    edge & 1 ? [bbox[0], a[1] + (b[1] - a[1]) * (bbox[0] - a[0]) / (b[0] - a[0])] : // left
44247                    null;
44248         }
44249
44250         // bit code reflects the point position relative to the bbox:
44251
44252         //         left  mid  right
44253         //    top  1001  1000  1010
44254         //    mid  0001  0000  0010
44255         // bottom  0101  0100  0110
44256
44257         function bitCode(p, bbox) {
44258             var code = 0;
44259
44260             if (p[0] < bbox[0]) { code |= 1; } // left
44261             else if (p[0] > bbox[2]) { code |= 2; } // right
44262
44263             if (p[1] < bbox[1]) { code |= 4; } // bottom
44264             else if (p[1] > bbox[3]) { code |= 8; } // top
44265
44266             return code;
44267         }
44268         lineclip_1.default = _default;
44269
44270         var bboxClip_1 = createCommonjsModule(function (module, exports) {
44271         var __importStar = (commonjsGlobal && commonjsGlobal.__importStar) || function (mod) {
44272             if (mod && mod.__esModule) { return mod; }
44273             var result = {};
44274             if (mod != null) { for (var k in mod) { if (Object.hasOwnProperty.call(mod, k)) { result[k] = mod[k]; } } }
44275             result["default"] = mod;
44276             return result;
44277         };
44278         Object.defineProperty(exports, "__esModule", { value: true });
44279
44280
44281         var lineclip = __importStar(lineclip_1);
44282         /**
44283          * Takes a {@link Feature} and a bbox and clips the feature to the bbox using
44284          * [lineclip](https://github.com/mapbox/lineclip).
44285          * May result in degenerate edges when clipping Polygons.
44286          *
44287          * @name bboxClip
44288          * @param {Feature<LineString|MultiLineString|Polygon|MultiPolygon>} feature feature to clip to the bbox
44289          * @param {BBox} bbox extent in [minX, minY, maxX, maxY] order
44290          * @returns {Feature<LineString|MultiLineString|Polygon|MultiPolygon>} clipped Feature
44291          * @example
44292          * var bbox = [0, 0, 10, 10];
44293          * var poly = turf.polygon([[[2, 2], [8, 4], [12, 8], [3, 7], [2, 2]]]);
44294          *
44295          * var clipped = turf.bboxClip(poly, bbox);
44296          *
44297          * //addToMap
44298          * var addToMap = [bbox, poly, clipped]
44299          */
44300         function bboxClip(feature, bbox) {
44301             var geom = invariant.getGeom(feature);
44302             var type = geom.type;
44303             var properties = feature.type === "Feature" ? feature.properties : {};
44304             var coords = geom.coordinates;
44305             switch (type) {
44306                 case "LineString":
44307                 case "MultiLineString":
44308                     var lines_1 = [];
44309                     if (type === "LineString") {
44310                         coords = [coords];
44311                     }
44312                     coords.forEach(function (line) {
44313                         lineclip.polyline(line, bbox, lines_1);
44314                     });
44315                     if (lines_1.length === 1) {
44316                         return helpers$1.lineString(lines_1[0], properties);
44317                     }
44318                     return helpers$1.multiLineString(lines_1, properties);
44319                 case "Polygon":
44320                     return helpers$1.polygon(clipPolygon(coords, bbox), properties);
44321                 case "MultiPolygon":
44322                     return helpers$1.multiPolygon(coords.map(function (poly) {
44323                         return clipPolygon(poly, bbox);
44324                     }), properties);
44325                 default:
44326                     throw new Error("geometry " + type + " not supported");
44327             }
44328         }
44329         exports.default = bboxClip;
44330         function clipPolygon(rings, bbox) {
44331             var outRings = [];
44332             for (var _i = 0, rings_1 = rings; _i < rings_1.length; _i++) {
44333                 var ring = rings_1[_i];
44334                 var clipped = lineclip.polygon(ring, bbox);
44335                 if (clipped.length > 0) {
44336                     if (clipped[0][0] !== clipped[clipped.length - 1][0] || clipped[0][1] !== clipped[clipped.length - 1][1]) {
44337                         clipped.push(clipped[0]);
44338                     }
44339                     if (clipped.length >= 4) {
44340                         outRings.push(clipped);
44341                     }
44342                 }
44343             }
44344             return outRings;
44345         }
44346         });
44347
44348         var fastJsonStableStringify = function (data, opts) {
44349             if (!opts) { opts = {}; }
44350             if (typeof opts === 'function') { opts = { cmp: opts }; }
44351             var cycles = (typeof opts.cycles === 'boolean') ? opts.cycles : false;
44352
44353             var cmp = opts.cmp && (function (f) {
44354                 return function (node) {
44355                     return function (a, b) {
44356                         var aobj = { key: a, value: node[a] };
44357                         var bobj = { key: b, value: node[b] };
44358                         return f(aobj, bobj);
44359                     };
44360                 };
44361             })(opts.cmp);
44362
44363             var seen = [];
44364             return (function stringify (node) {
44365                 if (node && node.toJSON && typeof node.toJSON === 'function') {
44366                     node = node.toJSON();
44367                 }
44368
44369                 if (node === undefined) { return; }
44370                 if (typeof node == 'number') { return isFinite(node) ? '' + node : 'null'; }
44371                 if (typeof node !== 'object') { return JSON.stringify(node); }
44372
44373                 var i, out;
44374                 if (Array.isArray(node)) {
44375                     out = '[';
44376                     for (i = 0; i < node.length; i++) {
44377                         if (i) { out += ','; }
44378                         out += stringify(node[i]) || 'null';
44379                     }
44380                     return out + ']';
44381                 }
44382
44383                 if (node === null) { return 'null'; }
44384
44385                 if (seen.indexOf(node) !== -1) {
44386                     if (cycles) { return JSON.stringify('__cycle__'); }
44387                     throw new TypeError('Converting circular structure to JSON');
44388                 }
44389
44390                 var seenIndex = seen.push(node) - 1;
44391                 var keys = Object.keys(node).sort(cmp && cmp(node));
44392                 out = '';
44393                 for (i = 0; i < keys.length; i++) {
44394                     var key = keys[i];
44395                     var value = stringify(node[key]);
44396
44397                     if (!value) { continue; }
44398                     if (out) { out += ','; }
44399                     out += JSON.stringify(key) + ':' + value;
44400                 }
44401                 seen.splice(seenIndex, 1);
44402                 return '{' + out + '}';
44403             })(data);
44404         };
44405
44406         function DEFAULT_COMPARE (a, b) { return a > b ? 1 : a < b ? -1 : 0; }
44407
44408         var SplayTree = function SplayTree(compare, noDuplicates) {
44409           if ( compare === void 0 ) compare = DEFAULT_COMPARE;
44410           if ( noDuplicates === void 0 ) noDuplicates = false;
44411
44412           this._compare = compare;
44413           this._root = null;
44414           this._size = 0;
44415           this._noDuplicates = !!noDuplicates;
44416         };
44417
44418         var prototypeAccessors = { size: { configurable: true } };
44419
44420
44421         SplayTree.prototype.rotateLeft = function rotateLeft (x) {
44422           var y = x.right;
44423           if (y) {
44424             x.right = y.left;
44425             if (y.left) { y.left.parent = x; }
44426             y.parent = x.parent;
44427           }
44428
44429           if (!x.parent)              { this._root = y; }
44430           else if (x === x.parent.left) { x.parent.left = y; }
44431           else                        { x.parent.right = y; }
44432           if (y) { y.left = x; }
44433           x.parent = y;
44434         };
44435
44436
44437         SplayTree.prototype.rotateRight = function rotateRight (x) {
44438           var y = x.left;
44439           if (y) {
44440             x.left = y.right;
44441             if (y.right) { y.right.parent = x; }
44442             y.parent = x.parent;
44443           }
44444
44445           if (!x.parent)             { this._root = y; }
44446           else if(x === x.parent.left) { x.parent.left = y; }
44447           else                       { x.parent.right = y; }
44448           if (y) { y.right = x; }
44449           x.parent = y;
44450         };
44451
44452
44453         SplayTree.prototype._splay = function _splay (x) {
44454           while (x.parent) {
44455             var p = x.parent;
44456             if (!p.parent) {
44457               if (p.left === x) { this.rotateRight(p); }
44458               else            { this.rotateLeft(p); }
44459             } else if (p.left === x && p.parent.left === p) {
44460               this.rotateRight(p.parent);
44461               this.rotateRight(p);
44462             } else if (p.right === x && p.parent.right === p) {
44463               this.rotateLeft(p.parent);
44464               this.rotateLeft(p);
44465             } else if (p.left === x && p.parent.right === p) {
44466               this.rotateRight(p);
44467               this.rotateLeft(p);
44468             } else {
44469               this.rotateLeft(p);
44470               this.rotateRight(p);
44471             }
44472           }
44473         };
44474
44475
44476         SplayTree.prototype.splay = function splay (x) {
44477           var p, gp, ggp, l, r;
44478
44479           while (x.parent) {
44480             p = x.parent;
44481             gp = p.parent;
44482
44483             if (gp && gp.parent) {
44484               ggp = gp.parent;
44485               if (ggp.left === gp) { ggp.left= x; }
44486               else               { ggp.right = x; }
44487               x.parent = ggp;
44488             } else {
44489               x.parent = null;
44490               this._root = x;
44491             }
44492
44493             l = x.left; r = x.right;
44494
44495             if (x === p.left) { // left
44496               if (gp) {
44497                 if (gp.left === p) {
44498                   /* zig-zig */
44499                   if (p.right) {
44500                     gp.left = p.right;
44501                     gp.left.parent = gp;
44502                   } else { gp.left = null; }
44503
44504                   p.right = gp;
44505                   gp.parent = p;
44506                 } else {
44507                   /* zig-zag */
44508                   if (l) {
44509                     gp.right = l;
44510                     l.parent = gp;
44511                   } else { gp.right = null; }
44512
44513                   x.left  = gp;
44514                   gp.parent = x;
44515                 }
44516               }
44517               if (r) {
44518                 p.left = r;
44519                 r.parent = p;
44520               } else { p.left = null; }
44521
44522               x.right= p;
44523               p.parent = x;
44524             } else { // right
44525               if (gp) {
44526                 if (gp.right === p) {
44527                   /* zig-zig */
44528                   if (p.left) {
44529                     gp.right = p.left;
44530                     gp.right.parent = gp;
44531                   } else { gp.right = null; }
44532
44533                   p.left = gp;
44534                   gp.parent = p;
44535                 } else {
44536                   /* zig-zag */
44537                   if (r) {
44538                     gp.left = r;
44539                     r.parent = gp;
44540                   } else { gp.left = null; }
44541
44542                   x.right = gp;
44543                   gp.parent = x;
44544                 }
44545               }
44546               if (l) {
44547                 p.right = l;
44548                 l.parent = p;
44549               } else { p.right = null; }
44550
44551               x.left = p;
44552               p.parent = x;
44553             }
44554           }
44555         };
44556
44557
44558         SplayTree.prototype.replace = function replace (u, v) {
44559           if (!u.parent) { this._root = v; }
44560           else if (u === u.parent.left) { u.parent.left = v; }
44561           else { u.parent.right = v; }
44562           if (v) { v.parent = u.parent; }
44563         };
44564
44565
44566         SplayTree.prototype.minNode = function minNode (u) {
44567             if ( u === void 0 ) u = this._root;
44568
44569           if (u) { while (u.left) { u = u.left; } }
44570           return u;
44571         };
44572
44573
44574         SplayTree.prototype.maxNode = function maxNode (u) {
44575             if ( u === void 0 ) u = this._root;
44576
44577           if (u) { while (u.right) { u = u.right; } }
44578           return u;
44579         };
44580
44581
44582         SplayTree.prototype.insert = function insert (key, data) {
44583           var z = this._root;
44584           var p = null;
44585           var comp = this._compare;
44586           var cmp;
44587
44588           if (this._noDuplicates) {
44589             while (z) {
44590               p = z;
44591               cmp = comp(z.key, key);
44592               if (cmp === 0) { return; }
44593               else if (comp(z.key, key) < 0) { z = z.right; }
44594               else { z = z.left; }
44595             }
44596           } else {
44597             while (z) {
44598               p = z;
44599               if (comp(z.key, key) < 0) { z = z.right; }
44600               else { z = z.left; }
44601             }
44602           }
44603
44604           z = { key: key, data: data, left: null, right: null, parent: p };
44605
44606           if (!p)                        { this._root = z; }
44607           else if (comp(p.key, z.key) < 0) { p.right = z; }
44608           else                           { p.left= z; }
44609
44610           this.splay(z);
44611           this._size++;
44612           return z;
44613         };
44614
44615
44616         SplayTree.prototype.find = function find (key) {
44617           var z  = this._root;
44618           var comp = this._compare;
44619           while (z) {
44620             var cmp = comp(z.key, key);
44621             if    (cmp < 0) { z = z.right; }
44622             else if (cmp > 0) { z = z.left; }
44623             else            { return z; }
44624           }
44625           return null;
44626         };
44627
44628         /**
44629          * Whether the tree contains a node with the given key
44630          * @param{Key} key
44631          * @return {boolean} true/false
44632          */
44633         SplayTree.prototype.contains = function contains (key) {
44634           var node     = this._root;
44635           var comparator = this._compare;
44636           while (node){
44637             var cmp = comparator(key, node.key);
44638             if    (cmp === 0) { return true; }
44639             else if (cmp < 0) { node = node.left; }
44640             else              { node = node.right; }
44641           }
44642
44643           return false;
44644         };
44645
44646
44647         SplayTree.prototype.remove = function remove (key) {
44648           var z = this.find(key);
44649
44650           if (!z) { return false; }
44651
44652           this.splay(z);
44653
44654           if (!z.left) { this.replace(z, z.right); }
44655           else if (!z.right) { this.replace(z, z.left); }
44656           else {
44657             var y = this.minNode(z.right);
44658             if (y.parent !== z) {
44659               this.replace(y, y.right);
44660               y.right = z.right;
44661               y.right.parent = y;
44662             }
44663             this.replace(z, y);
44664             y.left = z.left;
44665             y.left.parent = y;
44666           }
44667
44668           this._size--;
44669           return true;
44670         };
44671
44672
44673         SplayTree.prototype.removeNode = function removeNode (z) {
44674           if (!z) { return false; }
44675
44676           this.splay(z);
44677
44678           if (!z.left) { this.replace(z, z.right); }
44679           else if (!z.right) { this.replace(z, z.left); }
44680           else {
44681             var y = this.minNode(z.right);
44682             if (y.parent !== z) {
44683               this.replace(y, y.right);
44684               y.right = z.right;
44685               y.right.parent = y;
44686             }
44687             this.replace(z, y);
44688             y.left = z.left;
44689             y.left.parent = y;
44690           }
44691
44692           this._size--;
44693           return true;
44694         };
44695
44696
44697         SplayTree.prototype.erase = function erase (key) {
44698           var z = this.find(key);
44699           if (!z) { return; }
44700
44701           this.splay(z);
44702
44703           var s = z.left;
44704           var t = z.right;
44705
44706           var sMax = null;
44707           if (s) {
44708             s.parent = null;
44709             sMax = this.maxNode(s);
44710             this.splay(sMax);
44711             this._root = sMax;
44712           }
44713           if (t) {
44714             if (s) { sMax.right = t; }
44715             else { this._root = t; }
44716             t.parent = sMax;
44717           }
44718
44719           this._size--;
44720         };
44721
44722         /**
44723          * Removes and returns the node with smallest key
44724          * @return {?Node}
44725          */
44726         SplayTree.prototype.pop = function pop () {
44727           var node = this._root, returnValue = null;
44728           if (node) {
44729             while (node.left) { node = node.left; }
44730             returnValue = { key: node.key, data: node.data };
44731             this.remove(node.key);
44732           }
44733           return returnValue;
44734         };
44735
44736
44737         /* eslint-disable class-methods-use-this */
44738
44739         /**
44740          * Successor node
44741          * @param{Node} node
44742          * @return {?Node}
44743          */
44744         SplayTree.prototype.next = function next (node) {
44745           var successor = node;
44746           if (successor) {
44747             if (successor.right) {
44748               successor = successor.right;
44749               while (successor && successor.left) { successor = successor.left; }
44750             } else {
44751               successor = node.parent;
44752               while (successor && successor.right === node) {
44753                 node = successor; successor = successor.parent;
44754               }
44755             }
44756           }
44757           return successor;
44758         };
44759
44760
44761         /**
44762          * Predecessor node
44763          * @param{Node} node
44764          * @return {?Node}
44765          */
44766         SplayTree.prototype.prev = function prev (node) {
44767           var predecessor = node;
44768           if (predecessor) {
44769             if (predecessor.left) {
44770               predecessor = predecessor.left;
44771               while (predecessor && predecessor.right) { predecessor = predecessor.right; }
44772             } else {
44773               predecessor = node.parent;
44774               while (predecessor && predecessor.left === node) {
44775                 node = predecessor;
44776                 predecessor = predecessor.parent;
44777               }
44778             }
44779           }
44780           return predecessor;
44781         };
44782         /* eslint-enable class-methods-use-this */
44783
44784
44785         /**
44786          * @param{forEachCallback} callback
44787          * @return {SplayTree}
44788          */
44789         SplayTree.prototype.forEach = function forEach (callback) {
44790           var current = this._root;
44791           var s = [], done = false, i = 0;
44792
44793           while (!done) {
44794             // Reach the left most Node of the current Node
44795             if (current) {
44796               // Place pointer to a tree node on the stack
44797               // before traversing the node's left subtree
44798               s.push(current);
44799               current = current.left;
44800             } else {
44801               // BackTrack from the empty subtree and visit the Node
44802               // at the top of the stack; however, if the stack is
44803               // empty you are done
44804               if (s.length > 0) {
44805                 current = s.pop();
44806                 callback(current, i++);
44807
44808                 // We have visited the node and its left
44809                 // subtree. Now, it's right subtree's turn
44810                 current = current.right;
44811               } else { done = true; }
44812             }
44813           }
44814           return this;
44815         };
44816
44817
44818         /**
44819          * Walk key range from `low` to `high`. Stops if `fn` returns a value.
44820          * @param{Key}    low
44821          * @param{Key}    high
44822          * @param{Function} fn
44823          * @param{*?}     ctx
44824          * @return {SplayTree}
44825          */
44826         SplayTree.prototype.range = function range (low, high, fn, ctx) {
44827           var Q = [];
44828           var compare = this._compare;
44829           var node = this._root, cmp;
44830
44831           while (Q.length !== 0 || node) {
44832             if (node) {
44833               Q.push(node);
44834               node = node.left;
44835             } else {
44836               node = Q.pop();
44837               cmp = compare(node.key, high);
44838               if (cmp > 0) {
44839                 break;
44840               } else if (compare(node.key, low) >= 0) {
44841                 if (fn.call(ctx, node)) { return this; } // stop if smth is returned
44842               }
44843               node = node.right;
44844             }
44845           }
44846           return this;
44847         };
44848
44849         /**
44850          * Returns all keys in order
44851          * @return {Array<Key>}
44852          */
44853         SplayTree.prototype.keys = function keys () {
44854           var current = this._root;
44855           var s = [], r = [], done = false;
44856
44857           while (!done) {
44858             if (current) {
44859               s.push(current);
44860               current = current.left;
44861             } else {
44862               if (s.length > 0) {
44863                 current = s.pop();
44864                 r.push(current.key);
44865                 current = current.right;
44866               } else { done = true; }
44867             }
44868           }
44869           return r;
44870         };
44871
44872
44873         /**
44874          * Returns `data` fields of all nodes in order.
44875          * @return {Array<Value>}
44876          */
44877         SplayTree.prototype.values = function values () {
44878           var current = this._root;
44879           var s = [], r = [], done = false;
44880
44881           while (!done) {
44882             if (current) {
44883               s.push(current);
44884               current = current.left;
44885             } else {
44886               if (s.length > 0) {
44887                 current = s.pop();
44888                 r.push(current.data);
44889                 current = current.right;
44890               } else { done = true; }
44891             }
44892           }
44893           return r;
44894         };
44895
44896
44897         /**
44898          * Returns node at given index
44899          * @param{number} index
44900          * @return {?Node}
44901          */
44902         SplayTree.prototype.at = function at (index) {
44903           // removed after a consideration, more misleading than useful
44904           // index = index % this.size;
44905           // if (index < 0) index = this.size - index;
44906
44907           var current = this._root;
44908           var s = [], done = false, i = 0;
44909
44910           while (!done) {
44911             if (current) {
44912               s.push(current);
44913               current = current.left;
44914             } else {
44915               if (s.length > 0) {
44916                 current = s.pop();
44917                 if (i === index) { return current; }
44918                 i++;
44919                 current = current.right;
44920               } else { done = true; }
44921             }
44922           }
44923           return null;
44924         };
44925
44926         /**
44927          * Bulk-load items. Both array have to be same size
44928          * @param{Array<Key>}  keys
44929          * @param{Array<Value>}[values]
44930          * @param{Boolean}     [presort=false] Pre-sort keys and values, using
44931          *                                       tree's comparator. Sorting is done
44932          *                                       in-place
44933          * @return {AVLTree}
44934          */
44935         SplayTree.prototype.load = function load (keys, values, presort) {
44936             if ( keys === void 0 ) keys = [];
44937             if ( values === void 0 ) values = [];
44938             if ( presort === void 0 ) presort = false;
44939
44940           if (this._size !== 0) { throw new Error('bulk-load: tree is not empty'); }
44941           var size = keys.length;
44942           if (presort) { sort(keys, values, 0, size - 1, this._compare); }
44943           this._root = loadRecursive(null, keys, values, 0, size);
44944           this._size = size;
44945           return this;
44946         };
44947
44948
44949         SplayTree.prototype.min = function min () {
44950           var node = this.minNode(this._root);
44951           if (node) { return node.key; }
44952           else    { return null; }
44953         };
44954
44955
44956         SplayTree.prototype.max = function max () {
44957           var node = this.maxNode(this._root);
44958           if (node) { return node.key; }
44959           else    { return null; }
44960         };
44961
44962         SplayTree.prototype.isEmpty = function isEmpty () { return this._root === null; };
44963         prototypeAccessors.size.get = function () { return this._size; };
44964
44965
44966         /**
44967          * Create a tree and load it with items
44968          * @param{Array<Key>}        keys
44969          * @param{Array<Value>?}      [values]
44970
44971          * @param{Function?}          [comparator]
44972          * @param{Boolean?}           [presort=false] Pre-sort keys and values, using
44973          *                                             tree's comparator. Sorting is done
44974          *                                             in-place
44975          * @param{Boolean?}           [noDuplicates=false] Allow duplicates
44976          * @return {SplayTree}
44977          */
44978         SplayTree.createTree = function createTree (keys, values, comparator, presort, noDuplicates) {
44979           return new SplayTree(comparator, noDuplicates).load(keys, values, presort);
44980         };
44981
44982         Object.defineProperties( SplayTree.prototype, prototypeAccessors );
44983
44984
44985         function loadRecursive (parent, keys, values, start, end) {
44986           var size = end - start;
44987           if (size > 0) {
44988             var middle = start + Math.floor(size / 2);
44989             var key    = keys[middle];
44990             var data   = values[middle];
44991             var node   = { key: key, data: data, parent: parent };
44992             node.left    = loadRecursive(node, keys, values, start, middle);
44993             node.right   = loadRecursive(node, keys, values, middle + 1, end);
44994             return node;
44995           }
44996           return null;
44997         }
44998
44999
45000         function sort(keys, values, left, right, compare) {
45001           if (left >= right) { return; }
45002
45003           var pivot = keys[(left + right) >> 1];
45004           var i = left - 1;
45005           var j = right + 1;
45006
45007           while (true) {
45008             do { i++; } while (compare(keys[i], pivot) < 0);
45009             do { j--; } while (compare(keys[j], pivot) > 0);
45010             if (i >= j) { break; }
45011
45012             var tmp = keys[i];
45013             keys[i] = keys[j];
45014             keys[j] = tmp;
45015
45016             tmp = values[i];
45017             values[i] = values[j];
45018             values[j] = tmp;
45019           }
45020
45021           sort(keys, values,  left,     j, compare);
45022           sort(keys, values, j + 1, right, compare);
45023         }
45024
45025         var NORMAL               = 0;
45026         var NON_CONTRIBUTING     = 1;
45027         var SAME_TRANSITION      = 2;
45028         var DIFFERENT_TRANSITION = 3;
45029
45030         var INTERSECTION = 0;
45031         var UNION        = 1;
45032         var DIFFERENCE   = 2;
45033         var XOR          = 3;
45034
45035         /**
45036          * @param  {SweepEvent} event
45037          * @param  {SweepEvent} prev
45038          * @param  {Operation} operation
45039          */
45040         function computeFields (event, prev, operation) {
45041           // compute inOut and otherInOut fields
45042           if (prev === null) {
45043             event.inOut      = false;
45044             event.otherInOut = true;
45045
45046           // previous line segment in sweepline belongs to the same polygon
45047           } else {
45048             if (event.isSubject === prev.isSubject) {
45049               event.inOut      = !prev.inOut;
45050               event.otherInOut = prev.otherInOut;
45051
45052             // previous line segment in sweepline belongs to the clipping polygon
45053             } else {
45054               event.inOut      = !prev.otherInOut;
45055               event.otherInOut = prev.isVertical() ? !prev.inOut : prev.inOut;
45056             }
45057
45058             // compute prevInResult field
45059             if (prev) {
45060               event.prevInResult = (!inResult(prev, operation) || prev.isVertical())
45061                 ? prev.prevInResult : prev;
45062             }
45063           }
45064
45065           // check if the line segment belongs to the Boolean operation
45066           var isInResult = inResult(event, operation);
45067           if (isInResult) {
45068             event.resultTransition = determineResultTransition(event, operation);
45069           } else {
45070             event.resultTransition = 0;
45071           }
45072         }
45073
45074
45075         /* eslint-disable indent */
45076         function inResult(event, operation) {
45077           switch (event.type) {
45078             case NORMAL:
45079               switch (operation) {
45080                 case INTERSECTION:
45081                   return !event.otherInOut;
45082                 case UNION:
45083                   return event.otherInOut;
45084                 case DIFFERENCE:
45085                   // return (event.isSubject && !event.otherInOut) ||
45086                   //         (!event.isSubject && event.otherInOut);
45087                   return (event.isSubject && event.otherInOut) ||
45088                           (!event.isSubject && !event.otherInOut);
45089                 case XOR:
45090                   return true;
45091               }
45092               break;
45093             case SAME_TRANSITION:
45094               return operation === INTERSECTION || operation === UNION;
45095             case DIFFERENT_TRANSITION:
45096               return operation === DIFFERENCE;
45097             case NON_CONTRIBUTING:
45098               return false;
45099           }
45100           return false;
45101         }
45102         /* eslint-enable indent */
45103
45104
45105         function determineResultTransition(event, operation) {
45106           var thisIn = !event.inOut;
45107           var thatIn = !event.otherInOut;
45108
45109           var isIn;
45110           switch (operation) {
45111             case INTERSECTION:
45112               isIn = thisIn && thatIn; break;
45113             case UNION:
45114               isIn = thisIn || thatIn; break;
45115             case XOR:
45116               isIn = thisIn ^ thatIn; break;
45117             case DIFFERENCE:
45118               if (event.isSubject) {
45119                 isIn = thisIn && !thatIn;
45120               } else {
45121                 isIn = thatIn && !thisIn;
45122               }
45123               break;
45124           }
45125           return isIn ? +1 : -1;
45126         }
45127
45128         var SweepEvent = function SweepEvent (point, left, otherEvent, isSubject, edgeType) {
45129
45130           /**
45131            * Is left endpoint?
45132            * @type {Boolean}
45133            */
45134           this.left = left;
45135
45136           /**
45137            * @type {Array.<Number>}
45138            */
45139           this.point = point;
45140
45141           /**
45142            * Other edge reference
45143            * @type {SweepEvent}
45144            */
45145           this.otherEvent = otherEvent;
45146
45147           /**
45148            * Belongs to source or clipping polygon
45149            * @type {Boolean}
45150            */
45151           this.isSubject = isSubject;
45152
45153           /**
45154            * Edge contribution type
45155            * @type {Number}
45156            */
45157           this.type = edgeType || NORMAL;
45158
45159
45160           /**
45161            * In-out transition for the sweepline crossing polygon
45162            * @type {Boolean}
45163            */
45164           this.inOut = false;
45165
45166
45167           /**
45168            * @type {Boolean}
45169            */
45170           this.otherInOut = false;
45171
45172           /**
45173            * Previous event in result?
45174            * @type {SweepEvent}
45175            */
45176           this.prevInResult = null;
45177
45178           /**
45179            * Type of result transition (0 = not in result, +1 = out-in, -1, in-out)
45180            * @type {Number}
45181            */
45182           this.resultTransition = 0;
45183
45184           // connection step
45185
45186           /**
45187            * @type {Number}
45188            */
45189           this.otherPos = -1;
45190
45191           /**
45192            * @type {Number}
45193            */
45194           this.outputContourId = -1;
45195
45196           this.isExteriorRing = true; // TODO: Looks unused, remove?
45197         };
45198
45199         var prototypeAccessors$1 = { inResult: { configurable: true } };
45200
45201
45202         /**
45203          * @param{Array.<Number>}p
45204          * @return {Boolean}
45205          */
45206         SweepEvent.prototype.isBelow = function isBelow (p) {
45207           var p0 = this.point, p1 = this.otherEvent.point;
45208           return this.left
45209             ? (p0[0] - p[0]) * (p1[1] - p[1]) - (p1[0] - p[0]) * (p0[1] - p[1]) > 0
45210             // signedArea(this.point, this.otherEvent.point, p) > 0 :
45211             : (p1[0] - p[0]) * (p0[1] - p[1]) - (p0[0] - p[0]) * (p1[1] - p[1]) > 0;
45212             //signedArea(this.otherEvent.point, this.point, p) > 0;
45213         };
45214
45215
45216         /**
45217          * @param{Array.<Number>}p
45218          * @return {Boolean}
45219          */
45220         SweepEvent.prototype.isAbove = function isAbove (p) {
45221           return !this.isBelow(p);
45222         };
45223
45224
45225         /**
45226          * @return {Boolean}
45227          */
45228         SweepEvent.prototype.isVertical = function isVertical () {
45229           return this.point[0] === this.otherEvent.point[0];
45230         };
45231
45232
45233         /**
45234          * Does event belong to result?
45235          * @return {Boolean}
45236          */
45237         prototypeAccessors$1.inResult.get = function () {
45238           return this.resultTransition !== 0;
45239         };
45240
45241
45242         SweepEvent.prototype.clone = function clone () {
45243           var copy = new SweepEvent(
45244             this.point, this.left, this.otherEvent, this.isSubject, this.type);
45245
45246           copy.contourId      = this.contourId;
45247           copy.resultTransition = this.resultTransition;
45248           copy.prevInResult   = this.prevInResult;
45249           copy.isExteriorRing = this.isExteriorRing;
45250           copy.inOut          = this.inOut;
45251           copy.otherInOut     = this.otherInOut;
45252
45253           return copy;
45254         };
45255
45256         Object.defineProperties( SweepEvent.prototype, prototypeAccessors$1 );
45257
45258         function equals(p1, p2) {
45259           if (p1[0] === p2[0]) {
45260             if (p1[1] === p2[1]) {
45261               return true;
45262             } else {
45263               return false;
45264             }
45265           }
45266           return false;
45267         }
45268
45269         // const EPSILON = 1e-9;
45270         // const abs = Math.abs;
45271         // TODO https://github.com/w8r/martinez/issues/6#issuecomment-262847164
45272         // Precision problem.
45273         //
45274         // module.exports = function equals(p1, p2) {
45275         //   return abs(p1[0] - p2[0]) <= EPSILON && abs(p1[1] - p2[1]) <= EPSILON;
45276         // };
45277
45278         var epsilon$1 = 1.1102230246251565e-16;
45279         var splitter = 134217729;
45280         var resulterrbound = (3 + 8 * epsilon$1) * epsilon$1;
45281
45282         // fast_expansion_sum_zeroelim routine from oritinal code
45283         function sum$1(elen, e, flen, f, h) {
45284             var Q, Qnew, hh, bvirt;
45285             var enow = e[0];
45286             var fnow = f[0];
45287             var eindex = 0;
45288             var findex = 0;
45289             if ((fnow > enow) === (fnow > -enow)) {
45290                 Q = enow;
45291                 enow = e[++eindex];
45292             } else {
45293                 Q = fnow;
45294                 fnow = f[++findex];
45295             }
45296             var hindex = 0;
45297             if (eindex < elen && findex < flen) {
45298                 if ((fnow > enow) === (fnow > -enow)) {
45299                     Qnew = enow + Q;
45300                     hh = Q - (Qnew - enow);
45301                     enow = e[++eindex];
45302                 } else {
45303                     Qnew = fnow + Q;
45304                     hh = Q - (Qnew - fnow);
45305                     fnow = f[++findex];
45306                 }
45307                 Q = Qnew;
45308                 if (hh !== 0) {
45309                     h[hindex++] = hh;
45310                 }
45311                 while (eindex < elen && findex < flen) {
45312                     if ((fnow > enow) === (fnow > -enow)) {
45313                         Qnew = Q + enow;
45314                         bvirt = Qnew - Q;
45315                         hh = Q - (Qnew - bvirt) + (enow - bvirt);
45316                         enow = e[++eindex];
45317                     } else {
45318                         Qnew = Q + fnow;
45319                         bvirt = Qnew - Q;
45320                         hh = Q - (Qnew - bvirt) + (fnow - bvirt);
45321                         fnow = f[++findex];
45322                     }
45323                     Q = Qnew;
45324                     if (hh !== 0) {
45325                         h[hindex++] = hh;
45326                     }
45327                 }
45328             }
45329             while (eindex < elen) {
45330                 Qnew = Q + enow;
45331                 bvirt = Qnew - Q;
45332                 hh = Q - (Qnew - bvirt) + (enow - bvirt);
45333                 enow = e[++eindex];
45334                 Q = Qnew;
45335                 if (hh !== 0) {
45336                     h[hindex++] = hh;
45337                 }
45338             }
45339             while (findex < flen) {
45340                 Qnew = Q + fnow;
45341                 bvirt = Qnew - Q;
45342                 hh = Q - (Qnew - bvirt) + (fnow - bvirt);
45343                 fnow = f[++findex];
45344                 Q = Qnew;
45345                 if (hh !== 0) {
45346                     h[hindex++] = hh;
45347                 }
45348             }
45349             if (Q !== 0 || hindex === 0) {
45350                 h[hindex++] = Q;
45351             }
45352             return hindex;
45353         }
45354
45355         function estimate(elen, e) {
45356             var Q = e[0];
45357             for (var i = 1; i < elen; i++) { Q += e[i]; }
45358             return Q;
45359         }
45360
45361         function vec(n) {
45362             return new Float64Array(n);
45363         }
45364
45365         var ccwerrboundA = (3 + 16 * epsilon$1) * epsilon$1;
45366         var ccwerrboundB = (2 + 12 * epsilon$1) * epsilon$1;
45367         var ccwerrboundC = (9 + 64 * epsilon$1) * epsilon$1 * epsilon$1;
45368
45369         var B = vec(4);
45370         var C1 = vec(8);
45371         var C2 = vec(12);
45372         var D = vec(16);
45373         var u = vec(4);
45374
45375         function orient2dadapt(ax, ay, bx, by, cx, cy, detsum) {
45376             var acxtail, acytail, bcxtail, bcytail;
45377             var bvirt, c, ahi, alo, bhi, blo, _i, _j, _0, s1, s0, t1, t0, u3;
45378
45379             var acx = ax - cx;
45380             var bcx = bx - cx;
45381             var acy = ay - cy;
45382             var bcy = by - cy;
45383
45384             s1 = acx * bcy;
45385             c = splitter * acx;
45386             ahi = c - (c - acx);
45387             alo = acx - ahi;
45388             c = splitter * bcy;
45389             bhi = c - (c - bcy);
45390             blo = bcy - bhi;
45391             s0 = alo * blo - (s1 - ahi * bhi - alo * bhi - ahi * blo);
45392             t1 = acy * bcx;
45393             c = splitter * acy;
45394             ahi = c - (c - acy);
45395             alo = acy - ahi;
45396             c = splitter * bcx;
45397             bhi = c - (c - bcx);
45398             blo = bcx - bhi;
45399             t0 = alo * blo - (t1 - ahi * bhi - alo * bhi - ahi * blo);
45400             _i = s0 - t0;
45401             bvirt = s0 - _i;
45402             B[0] = s0 - (_i + bvirt) + (bvirt - t0);
45403             _j = s1 + _i;
45404             bvirt = _j - s1;
45405             _0 = s1 - (_j - bvirt) + (_i - bvirt);
45406             _i = _0 - t1;
45407             bvirt = _0 - _i;
45408             B[1] = _0 - (_i + bvirt) + (bvirt - t1);
45409             u3 = _j + _i;
45410             bvirt = u3 - _j;
45411             B[2] = _j - (u3 - bvirt) + (_i - bvirt);
45412             B[3] = u3;
45413
45414             var det = estimate(4, B);
45415             var errbound = ccwerrboundB * detsum;
45416             if (det >= errbound || -det >= errbound) {
45417                 return det;
45418             }
45419
45420             bvirt = ax - acx;
45421             acxtail = ax - (acx + bvirt) + (bvirt - cx);
45422             bvirt = bx - bcx;
45423             bcxtail = bx - (bcx + bvirt) + (bvirt - cx);
45424             bvirt = ay - acy;
45425             acytail = ay - (acy + bvirt) + (bvirt - cy);
45426             bvirt = by - bcy;
45427             bcytail = by - (bcy + bvirt) + (bvirt - cy);
45428
45429             if (acxtail === 0 && acytail === 0 && bcxtail === 0 && bcytail === 0) {
45430                 return det;
45431             }
45432
45433             errbound = ccwerrboundC * detsum + resulterrbound * Math.abs(det);
45434             det += (acx * bcytail + bcy * acxtail) - (acy * bcxtail + bcx * acytail);
45435             if (det >= errbound || -det >= errbound) { return det; }
45436
45437             s1 = acxtail * bcy;
45438             c = splitter * acxtail;
45439             ahi = c - (c - acxtail);
45440             alo = acxtail - ahi;
45441             c = splitter * bcy;
45442             bhi = c - (c - bcy);
45443             blo = bcy - bhi;
45444             s0 = alo * blo - (s1 - ahi * bhi - alo * bhi - ahi * blo);
45445             t1 = acytail * bcx;
45446             c = splitter * acytail;
45447             ahi = c - (c - acytail);
45448             alo = acytail - ahi;
45449             c = splitter * bcx;
45450             bhi = c - (c - bcx);
45451             blo = bcx - bhi;
45452             t0 = alo * blo - (t1 - ahi * bhi - alo * bhi - ahi * blo);
45453             _i = s0 - t0;
45454             bvirt = s0 - _i;
45455             u[0] = s0 - (_i + bvirt) + (bvirt - t0);
45456             _j = s1 + _i;
45457             bvirt = _j - s1;
45458             _0 = s1 - (_j - bvirt) + (_i - bvirt);
45459             _i = _0 - t1;
45460             bvirt = _0 - _i;
45461             u[1] = _0 - (_i + bvirt) + (bvirt - t1);
45462             u3 = _j + _i;
45463             bvirt = u3 - _j;
45464             u[2] = _j - (u3 - bvirt) + (_i - bvirt);
45465             u[3] = u3;
45466             var C1len = sum$1(4, B, 4, u, C1);
45467
45468             s1 = acx * bcytail;
45469             c = splitter * acx;
45470             ahi = c - (c - acx);
45471             alo = acx - ahi;
45472             c = splitter * bcytail;
45473             bhi = c - (c - bcytail);
45474             blo = bcytail - bhi;
45475             s0 = alo * blo - (s1 - ahi * bhi - alo * bhi - ahi * blo);
45476             t1 = acy * bcxtail;
45477             c = splitter * acy;
45478             ahi = c - (c - acy);
45479             alo = acy - ahi;
45480             c = splitter * bcxtail;
45481             bhi = c - (c - bcxtail);
45482             blo = bcxtail - bhi;
45483             t0 = alo * blo - (t1 - ahi * bhi - alo * bhi - ahi * blo);
45484             _i = s0 - t0;
45485             bvirt = s0 - _i;
45486             u[0] = s0 - (_i + bvirt) + (bvirt - t0);
45487             _j = s1 + _i;
45488             bvirt = _j - s1;
45489             _0 = s1 - (_j - bvirt) + (_i - bvirt);
45490             _i = _0 - t1;
45491             bvirt = _0 - _i;
45492             u[1] = _0 - (_i + bvirt) + (bvirt - t1);
45493             u3 = _j + _i;
45494             bvirt = u3 - _j;
45495             u[2] = _j - (u3 - bvirt) + (_i - bvirt);
45496             u[3] = u3;
45497             var C2len = sum$1(C1len, C1, 4, u, C2);
45498
45499             s1 = acxtail * bcytail;
45500             c = splitter * acxtail;
45501             ahi = c - (c - acxtail);
45502             alo = acxtail - ahi;
45503             c = splitter * bcytail;
45504             bhi = c - (c - bcytail);
45505             blo = bcytail - bhi;
45506             s0 = alo * blo - (s1 - ahi * bhi - alo * bhi - ahi * blo);
45507             t1 = acytail * bcxtail;
45508             c = splitter * acytail;
45509             ahi = c - (c - acytail);
45510             alo = acytail - ahi;
45511             c = splitter * bcxtail;
45512             bhi = c - (c - bcxtail);
45513             blo = bcxtail - bhi;
45514             t0 = alo * blo - (t1 - ahi * bhi - alo * bhi - ahi * blo);
45515             _i = s0 - t0;
45516             bvirt = s0 - _i;
45517             u[0] = s0 - (_i + bvirt) + (bvirt - t0);
45518             _j = s1 + _i;
45519             bvirt = _j - s1;
45520             _0 = s1 - (_j - bvirt) + (_i - bvirt);
45521             _i = _0 - t1;
45522             bvirt = _0 - _i;
45523             u[1] = _0 - (_i + bvirt) + (bvirt - t1);
45524             u3 = _j + _i;
45525             bvirt = u3 - _j;
45526             u[2] = _j - (u3 - bvirt) + (_i - bvirt);
45527             u[3] = u3;
45528             var Dlen = sum$1(C2len, C2, 4, u, D);
45529
45530             return D[Dlen - 1];
45531         }
45532
45533         function orient2d(ax, ay, bx, by, cx, cy) {
45534             var detleft = (ay - cy) * (bx - cx);
45535             var detright = (ax - cx) * (by - cy);
45536             var det = detleft - detright;
45537
45538             if (detleft === 0 || detright === 0 || (detleft > 0) !== (detright > 0)) { return det; }
45539
45540             var detsum = Math.abs(detleft + detright);
45541             if (Math.abs(det) >= ccwerrboundA * detsum) { return det; }
45542
45543             return -orient2dadapt(ax, ay, bx, by, cx, cy, detsum);
45544         }
45545
45546         /**
45547          * Signed area of the triangle (p0, p1, p2)
45548          * @param  {Array.<Number>} p0
45549          * @param  {Array.<Number>} p1
45550          * @param  {Array.<Number>} p2
45551          * @return {Number}
45552          */
45553         function signedArea(p0, p1, p2) {
45554           var res = orient2d(p0[0], p0[1], p1[0], p1[1], p2[0], p2[1]);
45555           if (res > 0) { return -1; }
45556           if (res < 0) { return 1; }
45557           return 0;
45558         }
45559
45560         /**
45561          * @param  {SweepEvent} e1
45562          * @param  {SweepEvent} e2
45563          * @return {Number}
45564          */
45565         function compareEvents(e1, e2) {
45566           var p1 = e1.point;
45567           var p2 = e2.point;
45568
45569           // Different x-coordinate
45570           if (p1[0] > p2[0]) { return 1; }
45571           if (p1[0] < p2[0]) { return -1; }
45572
45573           // Different points, but same x-coordinate
45574           // Event with lower y-coordinate is processed first
45575           if (p1[1] !== p2[1]) { return p1[1] > p2[1] ? 1 : -1; }
45576
45577           return specialCases(e1, e2, p1);
45578         }
45579
45580
45581         /* eslint-disable no-unused-vars */
45582         function specialCases(e1, e2, p1, p2) {
45583           // Same coordinates, but one is a left endpoint and the other is
45584           // a right endpoint. The right endpoint is processed first
45585           if (e1.left !== e2.left)
45586             { return e1.left ? 1 : -1; }
45587
45588           // const p2 = e1.otherEvent.point, p3 = e2.otherEvent.point;
45589           // const sa = (p1[0] - p3[0]) * (p2[1] - p3[1]) - (p2[0] - p3[0]) * (p1[1] - p3[1])
45590           // Same coordinates, both events
45591           // are left endpoints or right endpoints.
45592           // not collinear
45593           if (signedArea(p1, e1.otherEvent.point, e2.otherEvent.point) !== 0) {
45594             // the event associate to the bottom segment is processed first
45595             return (!e1.isBelow(e2.otherEvent.point)) ? 1 : -1;
45596           }
45597
45598           return (!e1.isSubject && e2.isSubject) ? 1 : -1;
45599         }
45600         /* eslint-enable no-unused-vars */
45601
45602         /**
45603          * @param  {SweepEvent} se
45604          * @param  {Array.<Number>} p
45605          * @param  {Queue} queue
45606          * @return {Queue}
45607          */
45608         function divideSegment(se, p, queue)  {
45609           var r = new SweepEvent(p, false, se,            se.isSubject);
45610           var l = new SweepEvent(p, true,  se.otherEvent, se.isSubject);
45611
45612           /* eslint-disable no-console */
45613           if (equals(se.point, se.otherEvent.point)) {
45614             console.warn('what is that, a collapsed segment?', se);
45615           }
45616           /* eslint-enable no-console */
45617
45618           r.contourId = l.contourId = se.contourId;
45619
45620           // avoid a rounding error. The left event would be processed after the right event
45621           if (compareEvents(l, se.otherEvent) > 0) {
45622             se.otherEvent.left = true;
45623             l.left = false;
45624           }
45625
45626           // avoid a rounding error. The left event would be processed after the right event
45627           // if (compareEvents(se, r) > 0) {}
45628
45629           se.otherEvent.otherEvent = l;
45630           se.otherEvent = r;
45631
45632           queue.push(l);
45633           queue.push(r);
45634
45635           return queue;
45636         }
45637
45638         //const EPS = 1e-9;
45639
45640         /**
45641          * Finds the magnitude of the cross product of two vectors (if we pretend
45642          * they're in three dimensions)
45643          *
45644          * @param {Object} a First vector
45645          * @param {Object} b Second vector
45646          * @private
45647          * @returns {Number} The magnitude of the cross product
45648          */
45649         function crossProduct(a, b) {
45650           return (a[0] * b[1]) - (a[1] * b[0]);
45651         }
45652
45653         /**
45654          * Finds the dot product of two vectors.
45655          *
45656          * @param {Object} a First vector
45657          * @param {Object} b Second vector
45658          * @private
45659          * @returns {Number} The dot product
45660          */
45661         function dotProduct(a, b) {
45662           return (a[0] * b[0]) + (a[1] * b[1]);
45663         }
45664
45665         /**
45666          * Finds the intersection (if any) between two line segments a and b, given the
45667          * line segments' end points a1, a2 and b1, b2.
45668          *
45669          * This algorithm is based on Schneider and Eberly.
45670          * http://www.cimec.org.ar/~ncalvo/Schneider_Eberly.pdf
45671          * Page 244.
45672          *
45673          * @param {Array.<Number>} a1 point of first line
45674          * @param {Array.<Number>} a2 point of first line
45675          * @param {Array.<Number>} b1 point of second line
45676          * @param {Array.<Number>} b2 point of second line
45677          * @param {Boolean=}       noEndpointTouch whether to skip single touchpoints
45678          *                                         (meaning connected segments) as
45679          *                                         intersections
45680          * @returns {Array.<Array.<Number>>|Null} If the lines intersect, the point of
45681          * intersection. If they overlap, the two end points of the overlapping segment.
45682          * Otherwise, null.
45683          */
45684         function intersection (a1, a2, b1, b2, noEndpointTouch) {
45685           // The algorithm expects our lines in the form P + sd, where P is a point,
45686           // s is on the interval [0, 1], and d is a vector.
45687           // We are passed two points. P can be the first point of each pair. The
45688           // vector, then, could be thought of as the distance (in x and y components)
45689           // from the first point to the second point.
45690           // So first, let's make our vectors:
45691           var va = [a2[0] - a1[0], a2[1] - a1[1]];
45692           var vb = [b2[0] - b1[0], b2[1] - b1[1]];
45693           // We also define a function to convert back to regular point form:
45694
45695           /* eslint-disable arrow-body-style */
45696
45697           function toPoint(p, s, d) {
45698             return [
45699               p[0] + s * d[0],
45700               p[1] + s * d[1]
45701             ];
45702           }
45703
45704           /* eslint-enable arrow-body-style */
45705
45706           // The rest is pretty much a straight port of the algorithm.
45707           var e = [b1[0] - a1[0], b1[1] - a1[1]];
45708           var kross    = crossProduct(va, vb);
45709           var sqrKross = kross * kross;
45710           var sqrLenA  = dotProduct(va, va);
45711           //const sqrLenB  = dotProduct(vb, vb);
45712
45713           // Check for line intersection. This works because of the properties of the
45714           // cross product -- specifically, two vectors are parallel if and only if the
45715           // cross product is the 0 vector. The full calculation involves relative error
45716           // to account for possible very small line segments. See Schneider & Eberly
45717           // for details.
45718           if (sqrKross > 0/* EPS * sqrLenB * sqLenA */) {
45719             // If they're not parallel, then (because these are line segments) they
45720             // still might not actually intersect. This code checks that the
45721             // intersection point of the lines is actually on both line segments.
45722             var s = crossProduct(e, vb) / kross;
45723             if (s < 0 || s > 1) {
45724               // not on line segment a
45725               return null;
45726             }
45727             var t = crossProduct(e, va) / kross;
45728             if (t < 0 || t > 1) {
45729               // not on line segment b
45730               return null;
45731             }
45732             if (s === 0 || s === 1) {
45733               // on an endpoint of line segment a
45734               return noEndpointTouch ? null : [toPoint(a1, s, va)];
45735             }
45736             if (t === 0 || t === 1) {
45737               // on an endpoint of line segment b
45738               return noEndpointTouch ? null : [toPoint(b1, t, vb)];
45739             }
45740             return [toPoint(a1, s, va)];
45741           }
45742
45743           // If we've reached this point, then the lines are either parallel or the
45744           // same, but the segments could overlap partially or fully, or not at all.
45745           // So we need to find the overlap, if any. To do that, we can use e, which is
45746           // the (vector) difference between the two initial points. If this is parallel
45747           // with the line itself, then the two lines are the same line, and there will
45748           // be overlap.
45749           //const sqrLenE = dotProduct(e, e);
45750           kross = crossProduct(e, va);
45751           sqrKross = kross * kross;
45752
45753           if (sqrKross > 0 /* EPS * sqLenB * sqLenE */) {
45754           // Lines are just parallel, not the same. No overlap.
45755             return null;
45756           }
45757
45758           var sa = dotProduct(va, e) / sqrLenA;
45759           var sb = sa + dotProduct(va, vb) / sqrLenA;
45760           var smin = Math.min(sa, sb);
45761           var smax = Math.max(sa, sb);
45762
45763           // this is, essentially, the FindIntersection acting on floats from
45764           // Schneider & Eberly, just inlined into this function.
45765           if (smin <= 1 && smax >= 0) {
45766
45767             // overlap on an end point
45768             if (smin === 1) {
45769               return noEndpointTouch ? null : [toPoint(a1, smin > 0 ? smin : 0, va)];
45770             }
45771
45772             if (smax === 0) {
45773               return noEndpointTouch ? null : [toPoint(a1, smax < 1 ? smax : 1, va)];
45774             }
45775
45776             if (noEndpointTouch && smin === 0 && smax === 1) { return null; }
45777
45778             // There's overlap on a segment -- two points of intersection. Return both.
45779             return [
45780               toPoint(a1, smin > 0 ? smin : 0, va),
45781               toPoint(a1, smax < 1 ? smax : 1, va)
45782             ];
45783           }
45784
45785           return null;
45786         }
45787
45788         /**
45789          * @param  {SweepEvent} se1
45790          * @param  {SweepEvent} se2
45791          * @param  {Queue}      queue
45792          * @return {Number}
45793          */
45794         function possibleIntersection (se1, se2, queue) {
45795           // that disallows self-intersecting polygons,
45796           // did cost us half a day, so I'll leave it
45797           // out of respect
45798           // if (se1.isSubject === se2.isSubject) return;
45799           var inter = intersection(
45800             se1.point, se1.otherEvent.point,
45801             se2.point, se2.otherEvent.point
45802           );
45803
45804           var nintersections = inter ? inter.length : 0;
45805           if (nintersections === 0) { return 0; } // no intersection
45806
45807           // the line segments intersect at an endpoint of both line segments
45808           if ((nintersections === 1) &&
45809               (equals(se1.point, se2.point) ||
45810                equals(se1.otherEvent.point, se2.otherEvent.point))) {
45811             return 0;
45812           }
45813
45814           if (nintersections === 2 && se1.isSubject === se2.isSubject) {
45815             // if(se1.contourId === se2.contourId){
45816             // console.warn('Edges of the same polygon overlap',
45817             //   se1.point, se1.otherEvent.point, se2.point, se2.otherEvent.point);
45818             // }
45819             //throw new Error('Edges of the same polygon overlap');
45820             return 0;
45821           }
45822
45823           // The line segments associated to se1 and se2 intersect
45824           if (nintersections === 1) {
45825
45826             // if the intersection point is not an endpoint of se1
45827             if (!equals(se1.point, inter[0]) && !equals(se1.otherEvent.point, inter[0])) {
45828               divideSegment(se1, inter[0], queue);
45829             }
45830
45831             // if the intersection point is not an endpoint of se2
45832             if (!equals(se2.point, inter[0]) && !equals(se2.otherEvent.point, inter[0])) {
45833               divideSegment(se2, inter[0], queue);
45834             }
45835             return 1;
45836           }
45837
45838           // The line segments associated to se1 and se2 overlap
45839           var events        = [];
45840           var leftCoincide  = false;
45841           var rightCoincide = false;
45842
45843           if (equals(se1.point, se2.point)) {
45844             leftCoincide = true; // linked
45845           } else if (compareEvents(se1, se2) === 1) {
45846             events.push(se2, se1);
45847           } else {
45848             events.push(se1, se2);
45849           }
45850
45851           if (equals(se1.otherEvent.point, se2.otherEvent.point)) {
45852             rightCoincide = true;
45853           } else if (compareEvents(se1.otherEvent, se2.otherEvent) === 1) {
45854             events.push(se2.otherEvent, se1.otherEvent);
45855           } else {
45856             events.push(se1.otherEvent, se2.otherEvent);
45857           }
45858
45859           if ((leftCoincide && rightCoincide) || leftCoincide) {
45860             // both line segments are equal or share the left endpoint
45861             se2.type = NON_CONTRIBUTING;
45862             se1.type = (se2.inOut === se1.inOut)
45863               ? SAME_TRANSITION : DIFFERENT_TRANSITION;
45864
45865             if (leftCoincide && !rightCoincide) {
45866               // honestly no idea, but changing events selection from [2, 1]
45867               // to [0, 1] fixes the overlapping self-intersecting polygons issue
45868               divideSegment(events[1].otherEvent, events[0].point, queue);
45869             }
45870             return 2;
45871           }
45872
45873           // the line segments share the right endpoint
45874           if (rightCoincide) {
45875             divideSegment(events[0], events[1].point, queue);
45876             return 3;
45877           }
45878
45879           // no line segment includes totally the other one
45880           if (events[0] !== events[3].otherEvent) {
45881             divideSegment(events[0], events[1].point, queue);
45882             divideSegment(events[1], events[2].point, queue);
45883             return 3;
45884           }
45885
45886           // one line segment includes the other one
45887           divideSegment(events[0], events[1].point, queue);
45888           divideSegment(events[3].otherEvent, events[2].point, queue);
45889
45890           return 3;
45891         }
45892
45893         /**
45894          * @param  {SweepEvent} le1
45895          * @param  {SweepEvent} le2
45896          * @return {Number}
45897          */
45898         function compareSegments(le1, le2) {
45899           if (le1 === le2) { return 0; }
45900
45901           // Segments are not collinear
45902           if (signedArea(le1.point, le1.otherEvent.point, le2.point) !== 0 ||
45903             signedArea(le1.point, le1.otherEvent.point, le2.otherEvent.point) !== 0) {
45904
45905             // If they share their left endpoint use the right endpoint to sort
45906             if (equals(le1.point, le2.point)) { return le1.isBelow(le2.otherEvent.point) ? -1 : 1; }
45907
45908             // Different left endpoint: use the left endpoint to sort
45909             if (le1.point[0] === le2.point[0]) { return le1.point[1] < le2.point[1] ? -1 : 1; }
45910
45911             // has the line segment associated to e1 been inserted
45912             // into S after the line segment associated to e2 ?
45913             if (compareEvents(le1, le2) === 1) { return le2.isAbove(le1.point) ? -1 : 1; }
45914
45915             // The line segment associated to e2 has been inserted
45916             // into S after the line segment associated to e1
45917             return le1.isBelow(le2.point) ? -1 : 1;
45918           }
45919
45920           if (le1.isSubject === le2.isSubject) { // same polygon
45921             var p1 = le1.point, p2 = le2.point;
45922             if (p1[0] === p2[0] && p1[1] === p2[1]/*equals(le1.point, le2.point)*/) {
45923               p1 = le1.otherEvent.point; p2 = le2.otherEvent.point;
45924               if (p1[0] === p2[0] && p1[1] === p2[1]) { return 0; }
45925               else { return le1.contourId > le2.contourId ? 1 : -1; }
45926             }
45927           } else { // Segments are collinear, but belong to separate polygons
45928             return le1.isSubject ? -1 : 1;
45929           }
45930
45931           return compareEvents(le1, le2) === 1 ? 1 : -1;
45932         }
45933
45934         function subdivide(eventQueue, subject, clipping, sbbox, cbbox, operation) {
45935           var sweepLine = new SplayTree(compareSegments);
45936           var sortedEvents = [];
45937
45938           var rightbound = Math.min(sbbox[2], cbbox[2]);
45939
45940           var prev, next, begin;
45941
45942           while (eventQueue.length !== 0) {
45943             var event = eventQueue.pop();
45944             sortedEvents.push(event);
45945
45946             // optimization by bboxes for intersection and difference goes here
45947             if ((operation === INTERSECTION && event.point[0] > rightbound) ||
45948                 (operation === DIFFERENCE   && event.point[0] > sbbox[2])) {
45949               break;
45950             }
45951
45952             if (event.left) {
45953               next  = prev = sweepLine.insert(event);
45954               begin = sweepLine.minNode();
45955
45956               if (prev !== begin) { prev = sweepLine.prev(prev); }
45957               else                { prev = null; }
45958
45959               next = sweepLine.next(next);
45960
45961               var prevEvent = prev ? prev.key : null;
45962               var prevprevEvent = (void 0);
45963               computeFields(event, prevEvent, operation);
45964               if (next) {
45965                 if (possibleIntersection(event, next.key, eventQueue) === 2) {
45966                   computeFields(event, prevEvent, operation);
45967                   computeFields(event, next.key, operation);
45968                 }
45969               }
45970
45971               if (prev) {
45972                 if (possibleIntersection(prev.key, event, eventQueue) === 2) {
45973                   var prevprev = prev;
45974                   if (prevprev !== begin) { prevprev = sweepLine.prev(prevprev); }
45975                   else                    { prevprev = null; }
45976
45977                   prevprevEvent = prevprev ? prevprev.key : null;
45978                   computeFields(prevEvent, prevprevEvent, operation);
45979                   computeFields(event,     prevEvent,     operation);
45980                 }
45981               }
45982             } else {
45983               event = event.otherEvent;
45984               next = prev = sweepLine.find(event);
45985
45986               if (prev && next) {
45987
45988                 if (prev !== begin) { prev = sweepLine.prev(prev); }
45989                 else                { prev = null; }
45990
45991                 next = sweepLine.next(next);
45992                 sweepLine.remove(event);
45993
45994                 if (next && prev) {
45995                   possibleIntersection(prev.key, next.key, eventQueue);
45996                 }
45997               }
45998             }
45999           }
46000           return sortedEvents;
46001         }
46002
46003         var Contour = function Contour() {
46004           this.points = [];
46005           this.holeIds = [];
46006           this.holeOf = null;
46007           this.depth = null;
46008         };
46009
46010         Contour.prototype.isExterior = function isExterior () {
46011           return this.holeOf == null;
46012         };
46013
46014         /**
46015          * @param  {Array.<SweepEvent>} sortedEvents
46016          * @return {Array.<SweepEvent>}
46017          */
46018         function orderEvents(sortedEvents) {
46019           var event, i, len, tmp;
46020           var resultEvents = [];
46021           for (i = 0, len = sortedEvents.length; i < len; i++) {
46022             event = sortedEvents[i];
46023             if ((event.left && event.inResult) ||
46024               (!event.left && event.otherEvent.inResult)) {
46025               resultEvents.push(event);
46026             }
46027           }
46028           // Due to overlapping edges the resultEvents array can be not wholly sorted
46029           var sorted = false;
46030           while (!sorted) {
46031             sorted = true;
46032             for (i = 0, len = resultEvents.length; i < len; i++) {
46033               if ((i + 1) < len &&
46034                 compareEvents(resultEvents[i], resultEvents[i + 1]) === 1) {
46035                 tmp = resultEvents[i];
46036                 resultEvents[i] = resultEvents[i + 1];
46037                 resultEvents[i + 1] = tmp;
46038                 sorted = false;
46039               }
46040             }
46041           }
46042
46043
46044           for (i = 0, len = resultEvents.length; i < len; i++) {
46045             event = resultEvents[i];
46046             event.otherPos = i;
46047           }
46048
46049           // imagine, the right event is found in the beginning of the queue,
46050           // when his left counterpart is not marked yet
46051           for (i = 0, len = resultEvents.length; i < len; i++) {
46052             event = resultEvents[i];
46053             if (!event.left) {
46054               tmp = event.otherPos;
46055               event.otherPos = event.otherEvent.otherPos;
46056               event.otherEvent.otherPos = tmp;
46057             }
46058           }
46059
46060           return resultEvents;
46061         }
46062
46063
46064         /**
46065          * @param  {Number} pos
46066          * @param  {Array.<SweepEvent>} resultEvents
46067          * @param  {Object>}    processed
46068          * @return {Number}
46069          */
46070         function nextPos(pos, resultEvents, processed, origPos) {
46071           var newPos = pos + 1,
46072               p = resultEvents[pos].point,
46073               p1;
46074           var length = resultEvents.length;
46075
46076           if (newPos < length)
46077             { p1 = resultEvents[newPos].point; }
46078
46079           while (newPos < length && p1[0] === p[0] && p1[1] === p[1]) {
46080             if (!processed[newPos]) {
46081               return newPos;
46082             } else   {
46083               newPos++;
46084             }
46085             p1 = resultEvents[newPos].point;
46086           }
46087
46088           newPos = pos - 1;
46089
46090           while (processed[newPos] && newPos > origPos) {
46091             newPos--;
46092           }
46093
46094           return newPos;
46095         }
46096
46097
46098         function initializeContourFromContext(event, contours, contourId) {
46099           var contour = new Contour();
46100           if (event.prevInResult != null) {
46101             var prevInResult = event.prevInResult;
46102             // Note that it is valid to query the "previous in result" for its output contour id,
46103             // because we must have already processed it (i.e., assigned an output contour id)
46104             // in an earlier iteration, otherwise it wouldn't be possible that it is "previous in
46105             // result".
46106             var lowerContourId = prevInResult.outputContourId;
46107             var lowerResultTransition = prevInResult.resultTransition;
46108             if (lowerResultTransition > 0) {
46109               // We are inside. Now we have to check if the thing below us is another hole or
46110               // an exterior contour.
46111               var lowerContour = contours[lowerContourId];
46112               if (lowerContour.holeOf != null) {
46113                 // The lower contour is a hole => Connect the new contour as a hole to its parent,
46114                 // and use same depth.
46115                 var parentContourId = lowerContour.holeOf;
46116                 contours[parentContourId].holeIds.push(contourId);
46117                 contour.holeOf = parentContourId;
46118                 contour.depth = contours[lowerContourId].depth;
46119               } else {
46120                 // The lower contour is an exterior contour => Connect the new contour as a hole,
46121                 // and increment depth.
46122                 contours[lowerContourId].holeIds.push(contourId);
46123                 contour.holeOf = lowerContourId;
46124                 contour.depth = contours[lowerContourId].depth + 1;
46125               }
46126             } else {
46127               // We are outside => this contour is an exterior contour of same depth.
46128               contour.holeOf = null;
46129               contour.depth = contours[lowerContourId].depth;
46130             }
46131           } else {
46132             // There is no lower/previous contour => this contour is an exterior contour of depth 0.
46133             contour.holeOf = null;
46134             contour.depth = 0;
46135           }
46136           return contour;
46137         }
46138
46139         /**
46140          * @param  {Array.<SweepEvent>} sortedEvents
46141          * @return {Array.<*>} polygons
46142          */
46143         function connectEdges(sortedEvents) {
46144           var i, len;
46145           var resultEvents = orderEvents(sortedEvents);
46146
46147           // "false"-filled array
46148           var processed = {};
46149           var contours = [];
46150
46151           var loop = function (  ) {
46152
46153             if (processed[i]) {
46154               return;
46155             }
46156
46157             var contourId = contours.length;
46158             var contour = initializeContourFromContext(resultEvents[i], contours, contourId);
46159
46160             // Helper function that combines marking an event as processed with assigning its output contour ID
46161             var markAsProcessed = function (pos) {
46162               processed[pos] = true;
46163               resultEvents[pos].outputContourId = contourId;
46164             };
46165
46166             var pos = i;
46167             var origPos = i;
46168
46169             var initial = resultEvents[i].point;
46170             contour.points.push(initial);
46171
46172             /* eslint no-constant-condition: "off" */
46173             while (true) {
46174               markAsProcessed(pos);
46175
46176               pos = resultEvents[pos].otherPos;
46177
46178               markAsProcessed(pos);
46179               contour.points.push(resultEvents[pos].point);
46180
46181               pos = nextPos(pos, resultEvents, processed, origPos);
46182
46183               if (pos == origPos) {
46184                 break;
46185               }
46186             }
46187
46188             contours.push(contour);
46189           };
46190
46191           for (i = 0, len = resultEvents.length; i < len; i++) loop(  );
46192
46193           return contours;
46194         }
46195
46196         var tinyqueue = TinyQueue;
46197         var _default$1 = TinyQueue;
46198
46199         function TinyQueue(data, compare) {
46200             if (!(this instanceof TinyQueue)) { return new TinyQueue(data, compare); }
46201
46202             this.data = data || [];
46203             this.length = this.data.length;
46204             this.compare = compare || defaultCompare$1;
46205
46206             if (this.length > 0) {
46207                 for (var i = (this.length >> 1) - 1; i >= 0; i--) { this._down(i); }
46208             }
46209         }
46210
46211         function defaultCompare$1(a, b) {
46212             return a < b ? -1 : a > b ? 1 : 0;
46213         }
46214
46215         TinyQueue.prototype = {
46216
46217             push: function (item) {
46218                 this.data.push(item);
46219                 this.length++;
46220                 this._up(this.length - 1);
46221             },
46222
46223             pop: function () {
46224                 if (this.length === 0) { return undefined; }
46225
46226                 var top = this.data[0];
46227                 this.length--;
46228
46229                 if (this.length > 0) {
46230                     this.data[0] = this.data[this.length];
46231                     this._down(0);
46232                 }
46233                 this.data.pop();
46234
46235                 return top;
46236             },
46237
46238             peek: function () {
46239                 return this.data[0];
46240             },
46241
46242             _up: function (pos) {
46243                 var data = this.data;
46244                 var compare = this.compare;
46245                 var item = data[pos];
46246
46247                 while (pos > 0) {
46248                     var parent = (pos - 1) >> 1;
46249                     var current = data[parent];
46250                     if (compare(item, current) >= 0) { break; }
46251                     data[pos] = current;
46252                     pos = parent;
46253                 }
46254
46255                 data[pos] = item;
46256             },
46257
46258             _down: function (pos) {
46259                 var data = this.data;
46260                 var compare = this.compare;
46261                 var halfLength = this.length >> 1;
46262                 var item = data[pos];
46263
46264                 while (pos < halfLength) {
46265                     var left = (pos << 1) + 1;
46266                     var right = left + 1;
46267                     var best = data[left];
46268
46269                     if (right < this.length && compare(data[right], best) < 0) {
46270                         left = right;
46271                         best = data[right];
46272                     }
46273                     if (compare(best, item) >= 0) { break; }
46274
46275                     data[pos] = best;
46276                     pos = left;
46277                 }
46278
46279                 data[pos] = item;
46280             }
46281         };
46282         tinyqueue.default = _default$1;
46283
46284         var max$2 = Math.max;
46285         var min = Math.min;
46286
46287         var contourId = 0;
46288
46289
46290         function processPolygon(contourOrHole, isSubject, depth, Q, bbox, isExteriorRing) {
46291           var i, len, s1, s2, e1, e2;
46292           for (i = 0, len = contourOrHole.length - 1; i < len; i++) {
46293             s1 = contourOrHole[i];
46294             s2 = contourOrHole[i + 1];
46295             e1 = new SweepEvent(s1, false, undefined, isSubject);
46296             e2 = new SweepEvent(s2, false, e1,        isSubject);
46297             e1.otherEvent = e2;
46298
46299             if (s1[0] === s2[0] && s1[1] === s2[1]) {
46300               continue; // skip collapsed edges, or it breaks
46301             }
46302
46303             e1.contourId = e2.contourId = depth;
46304             if (!isExteriorRing) {
46305               e1.isExteriorRing = false;
46306               e2.isExteriorRing = false;
46307             }
46308             if (compareEvents(e1, e2) > 0) {
46309               e2.left = true;
46310             } else {
46311               e1.left = true;
46312             }
46313
46314             var x = s1[0], y = s1[1];
46315             bbox[0] = min(bbox[0], x);
46316             bbox[1] = min(bbox[1], y);
46317             bbox[2] = max$2(bbox[2], x);
46318             bbox[3] = max$2(bbox[3], y);
46319
46320             // Pushing it so the queue is sorted from left to right,
46321             // with object on the left having the highest priority.
46322             Q.push(e1);
46323             Q.push(e2);
46324           }
46325         }
46326
46327
46328         function fillQueue(subject, clipping, sbbox, cbbox, operation) {
46329           var eventQueue = new tinyqueue(null, compareEvents);
46330           var polygonSet, isExteriorRing, i, ii, j, jj; //, k, kk;
46331
46332           for (i = 0, ii = subject.length; i < ii; i++) {
46333             polygonSet = subject[i];
46334             for (j = 0, jj = polygonSet.length; j < jj; j++) {
46335               isExteriorRing = j === 0;
46336               if (isExteriorRing) { contourId++; }
46337               processPolygon(polygonSet[j], true, contourId, eventQueue, sbbox, isExteriorRing);
46338             }
46339           }
46340
46341           for (i = 0, ii = clipping.length; i < ii; i++) {
46342             polygonSet = clipping[i];
46343             for (j = 0, jj = polygonSet.length; j < jj; j++) {
46344               isExteriorRing = j === 0;
46345               if (operation === DIFFERENCE) { isExteriorRing = false; }
46346               if (isExteriorRing) { contourId++; }
46347               processPolygon(polygonSet[j], false, contourId, eventQueue, cbbox, isExteriorRing);
46348             }
46349           }
46350
46351           return eventQueue;
46352         }
46353
46354         var EMPTY = [];
46355
46356
46357         function trivialOperation(subject, clipping, operation) {
46358           var result = null;
46359           if (subject.length * clipping.length === 0) {
46360             if        (operation === INTERSECTION) {
46361               result = EMPTY;
46362             } else if (operation === DIFFERENCE) {
46363               result = subject;
46364             } else if (operation === UNION ||
46365                        operation === XOR) {
46366               result = (subject.length === 0) ? clipping : subject;
46367             }
46368           }
46369           return result;
46370         }
46371
46372
46373         function compareBBoxes(subject, clipping, sbbox, cbbox, operation) {
46374           var result = null;
46375           if (sbbox[0] > cbbox[2] ||
46376               cbbox[0] > sbbox[2] ||
46377               sbbox[1] > cbbox[3] ||
46378               cbbox[1] > sbbox[3]) {
46379             if        (operation === INTERSECTION) {
46380               result = EMPTY;
46381             } else if (operation === DIFFERENCE) {
46382               result = subject;
46383             } else if (operation === UNION ||
46384                        operation === XOR) {
46385               result = subject.concat(clipping);
46386             }
46387           }
46388           return result;
46389         }
46390
46391
46392         function boolean(subject, clipping, operation) {
46393           if (typeof subject[0][0][0] === 'number') {
46394             subject = [subject];
46395           }
46396           if (typeof clipping[0][0][0] === 'number') {
46397             clipping = [clipping];
46398           }
46399           var trivial = trivialOperation(subject, clipping, operation);
46400           if (trivial) {
46401             return trivial === EMPTY ? null : trivial;
46402           }
46403           var sbbox = [Infinity, Infinity, -Infinity, -Infinity];
46404           var cbbox = [Infinity, Infinity, -Infinity, -Infinity];
46405
46406           // console.time('fill queue');
46407           var eventQueue = fillQueue(subject, clipping, sbbox, cbbox, operation);
46408           //console.timeEnd('fill queue');
46409
46410           trivial = compareBBoxes(subject, clipping, sbbox, cbbox, operation);
46411           if (trivial) {
46412             return trivial === EMPTY ? null : trivial;
46413           }
46414           // console.time('subdivide edges');
46415           var sortedEvents = subdivide(eventQueue, subject, clipping, sbbox, cbbox, operation);
46416           //console.timeEnd('subdivide edges');
46417
46418           // console.time('connect vertices');
46419           var contours = connectEdges(sortedEvents);
46420           //console.timeEnd('connect vertices');
46421
46422           // Convert contours to polygons
46423           var polygons = [];
46424           for (var i = 0; i < contours.length; i++) {
46425             var contour = contours[i];
46426             if (contour.isExterior()) {
46427               // The exterior ring goes first
46428               var rings = [contour.points];
46429               // Followed by holes if any
46430               for (var j = 0; j < contour.holeIds.length; j++) {
46431                 var holeId = contour.holeIds[j];
46432                 rings.push(contours[holeId].points);
46433               }
46434               polygons.push(rings);
46435             }
46436           }
46437
46438           return polygons;
46439         }
46440
46441         function union (subject, clipping) {
46442           return boolean(subject, clipping, UNION);
46443         }
46444
46445         var read$6 = function (buffer, offset, isLE, mLen, nBytes) {
46446           var e, m;
46447           var eLen = (nBytes * 8) - mLen - 1;
46448           var eMax = (1 << eLen) - 1;
46449           var eBias = eMax >> 1;
46450           var nBits = -7;
46451           var i = isLE ? (nBytes - 1) : 0;
46452           var d = isLE ? -1 : 1;
46453           var s = buffer[offset + i];
46454
46455           i += d;
46456
46457           e = s & ((1 << (-nBits)) - 1);
46458           s >>= (-nBits);
46459           nBits += eLen;
46460           for (; nBits > 0; e = (e * 256) + buffer[offset + i], i += d, nBits -= 8) {}
46461
46462           m = e & ((1 << (-nBits)) - 1);
46463           e >>= (-nBits);
46464           nBits += mLen;
46465           for (; nBits > 0; m = (m * 256) + buffer[offset + i], i += d, nBits -= 8) {}
46466
46467           if (e === 0) {
46468             e = 1 - eBias;
46469           } else if (e === eMax) {
46470             return m ? NaN : ((s ? -1 : 1) * Infinity)
46471           } else {
46472             m = m + Math.pow(2, mLen);
46473             e = e - eBias;
46474           }
46475           return (s ? -1 : 1) * m * Math.pow(2, e - mLen)
46476         };
46477
46478         var write$6 = function (buffer, value, offset, isLE, mLen, nBytes) {
46479           var e, m, c;
46480           var eLen = (nBytes * 8) - mLen - 1;
46481           var eMax = (1 << eLen) - 1;
46482           var eBias = eMax >> 1;
46483           var rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0);
46484           var i = isLE ? 0 : (nBytes - 1);
46485           var d = isLE ? 1 : -1;
46486           var s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0;
46487
46488           value = Math.abs(value);
46489
46490           if (isNaN(value) || value === Infinity) {
46491             m = isNaN(value) ? 1 : 0;
46492             e = eMax;
46493           } else {
46494             e = Math.floor(Math.log(value) / Math.LN2);
46495             if (value * (c = Math.pow(2, -e)) < 1) {
46496               e--;
46497               c *= 2;
46498             }
46499             if (e + eBias >= 1) {
46500               value += rt / c;
46501             } else {
46502               value += rt * Math.pow(2, 1 - eBias);
46503             }
46504             if (value * c >= 2) {
46505               e++;
46506               c /= 2;
46507             }
46508
46509             if (e + eBias >= eMax) {
46510               m = 0;
46511               e = eMax;
46512             } else if (e + eBias >= 1) {
46513               m = ((value * c) - 1) * Math.pow(2, mLen);
46514               e = e + eBias;
46515             } else {
46516               m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen);
46517               e = 0;
46518             }
46519           }
46520
46521           for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8) {}
46522
46523           e = (e << mLen) | m;
46524           eLen += mLen;
46525           for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8) {}
46526
46527           buffer[offset + i - d] |= s * 128;
46528         };
46529
46530         var ieee754 = {
46531                 read: read$6,
46532                 write: write$6
46533         };
46534
46535         var pbf = Pbf;
46536
46537
46538
46539         function Pbf(buf) {
46540             this.buf = ArrayBuffer.isView && ArrayBuffer.isView(buf) ? buf : new Uint8Array(buf || 0);
46541             this.pos = 0;
46542             this.type = 0;
46543             this.length = this.buf.length;
46544         }
46545
46546         Pbf.Varint  = 0; // varint: int32, int64, uint32, uint64, sint32, sint64, bool, enum
46547         Pbf.Fixed64 = 1; // 64-bit: double, fixed64, sfixed64
46548         Pbf.Bytes   = 2; // length-delimited: string, bytes, embedded messages, packed repeated fields
46549         Pbf.Fixed32 = 5; // 32-bit: float, fixed32, sfixed32
46550
46551         var SHIFT_LEFT_32 = (1 << 16) * (1 << 16),
46552             SHIFT_RIGHT_32 = 1 / SHIFT_LEFT_32;
46553
46554         // Threshold chosen based on both benchmarking and knowledge about browser string
46555         // data structures (which currently switch structure types at 12 bytes or more)
46556         var TEXT_DECODER_MIN_LENGTH = 12;
46557         var utf8TextDecoder = typeof TextDecoder === 'undefined' ? null : new TextDecoder('utf8');
46558
46559         Pbf.prototype = {
46560
46561             destroy: function() {
46562                 this.buf = null;
46563             },
46564
46565             // === READING =================================================================
46566
46567             readFields: function(readField, result, end) {
46568                 end = end || this.length;
46569
46570                 while (this.pos < end) {
46571                     var val = this.readVarint(),
46572                         tag = val >> 3,
46573                         startPos = this.pos;
46574
46575                     this.type = val & 0x7;
46576                     readField(tag, result, this);
46577
46578                     if (this.pos === startPos) { this.skip(val); }
46579                 }
46580                 return result;
46581             },
46582
46583             readMessage: function(readField, result) {
46584                 return this.readFields(readField, result, this.readVarint() + this.pos);
46585             },
46586
46587             readFixed32: function() {
46588                 var val = readUInt32(this.buf, this.pos);
46589                 this.pos += 4;
46590                 return val;
46591             },
46592
46593             readSFixed32: function() {
46594                 var val = readInt32(this.buf, this.pos);
46595                 this.pos += 4;
46596                 return val;
46597             },
46598
46599             // 64-bit int handling is based on github.com/dpw/node-buffer-more-ints (MIT-licensed)
46600
46601             readFixed64: function() {
46602                 var val = readUInt32(this.buf, this.pos) + readUInt32(this.buf, this.pos + 4) * SHIFT_LEFT_32;
46603                 this.pos += 8;
46604                 return val;
46605             },
46606
46607             readSFixed64: function() {
46608                 var val = readUInt32(this.buf, this.pos) + readInt32(this.buf, this.pos + 4) * SHIFT_LEFT_32;
46609                 this.pos += 8;
46610                 return val;
46611             },
46612
46613             readFloat: function() {
46614                 var val = ieee754.read(this.buf, this.pos, true, 23, 4);
46615                 this.pos += 4;
46616                 return val;
46617             },
46618
46619             readDouble: function() {
46620                 var val = ieee754.read(this.buf, this.pos, true, 52, 8);
46621                 this.pos += 8;
46622                 return val;
46623             },
46624
46625             readVarint: function(isSigned) {
46626                 var buf = this.buf,
46627                     val, b;
46628
46629                 b = buf[this.pos++]; val  =  b & 0x7f;        if (b < 0x80) { return val; }
46630                 b = buf[this.pos++]; val |= (b & 0x7f) << 7;  if (b < 0x80) { return val; }
46631                 b = buf[this.pos++]; val |= (b & 0x7f) << 14; if (b < 0x80) { return val; }
46632                 b = buf[this.pos++]; val |= (b & 0x7f) << 21; if (b < 0x80) { return val; }
46633                 b = buf[this.pos];   val |= (b & 0x0f) << 28;
46634
46635                 return readVarintRemainder(val, isSigned, this);
46636             },
46637
46638             readVarint64: function() { // for compatibility with v2.0.1
46639                 return this.readVarint(true);
46640             },
46641
46642             readSVarint: function() {
46643                 var num = this.readVarint();
46644                 return num % 2 === 1 ? (num + 1) / -2 : num / 2; // zigzag encoding
46645             },
46646
46647             readBoolean: function() {
46648                 return Boolean(this.readVarint());
46649             },
46650
46651             readString: function() {
46652                 var end = this.readVarint() + this.pos;
46653                 var pos = this.pos;
46654                 this.pos = end;
46655
46656                 if (end - pos >= TEXT_DECODER_MIN_LENGTH && utf8TextDecoder) {
46657                     // longer strings are fast with the built-in browser TextDecoder API
46658                     return readUtf8TextDecoder(this.buf, pos, end);
46659                 }
46660                 // short strings are fast with our custom implementation
46661                 return readUtf8(this.buf, pos, end);
46662             },
46663
46664             readBytes: function() {
46665                 var end = this.readVarint() + this.pos,
46666                     buffer = this.buf.subarray(this.pos, end);
46667                 this.pos = end;
46668                 return buffer;
46669             },
46670
46671             // verbose for performance reasons; doesn't affect gzipped size
46672
46673             readPackedVarint: function(arr, isSigned) {
46674                 if (this.type !== Pbf.Bytes) { return arr.push(this.readVarint(isSigned)); }
46675                 var end = readPackedEnd(this);
46676                 arr = arr || [];
46677                 while (this.pos < end) { arr.push(this.readVarint(isSigned)); }
46678                 return arr;
46679             },
46680             readPackedSVarint: function(arr) {
46681                 if (this.type !== Pbf.Bytes) { return arr.push(this.readSVarint()); }
46682                 var end = readPackedEnd(this);
46683                 arr = arr || [];
46684                 while (this.pos < end) { arr.push(this.readSVarint()); }
46685                 return arr;
46686             },
46687             readPackedBoolean: function(arr) {
46688                 if (this.type !== Pbf.Bytes) { return arr.push(this.readBoolean()); }
46689                 var end = readPackedEnd(this);
46690                 arr = arr || [];
46691                 while (this.pos < end) { arr.push(this.readBoolean()); }
46692                 return arr;
46693             },
46694             readPackedFloat: function(arr) {
46695                 if (this.type !== Pbf.Bytes) { return arr.push(this.readFloat()); }
46696                 var end = readPackedEnd(this);
46697                 arr = arr || [];
46698                 while (this.pos < end) { arr.push(this.readFloat()); }
46699                 return arr;
46700             },
46701             readPackedDouble: function(arr) {
46702                 if (this.type !== Pbf.Bytes) { return arr.push(this.readDouble()); }
46703                 var end = readPackedEnd(this);
46704                 arr = arr || [];
46705                 while (this.pos < end) { arr.push(this.readDouble()); }
46706                 return arr;
46707             },
46708             readPackedFixed32: function(arr) {
46709                 if (this.type !== Pbf.Bytes) { return arr.push(this.readFixed32()); }
46710                 var end = readPackedEnd(this);
46711                 arr = arr || [];
46712                 while (this.pos < end) { arr.push(this.readFixed32()); }
46713                 return arr;
46714             },
46715             readPackedSFixed32: function(arr) {
46716                 if (this.type !== Pbf.Bytes) { return arr.push(this.readSFixed32()); }
46717                 var end = readPackedEnd(this);
46718                 arr = arr || [];
46719                 while (this.pos < end) { arr.push(this.readSFixed32()); }
46720                 return arr;
46721             },
46722             readPackedFixed64: function(arr) {
46723                 if (this.type !== Pbf.Bytes) { return arr.push(this.readFixed64()); }
46724                 var end = readPackedEnd(this);
46725                 arr = arr || [];
46726                 while (this.pos < end) { arr.push(this.readFixed64()); }
46727                 return arr;
46728             },
46729             readPackedSFixed64: function(arr) {
46730                 if (this.type !== Pbf.Bytes) { return arr.push(this.readSFixed64()); }
46731                 var end = readPackedEnd(this);
46732                 arr = arr || [];
46733                 while (this.pos < end) { arr.push(this.readSFixed64()); }
46734                 return arr;
46735             },
46736
46737             skip: function(val) {
46738                 var type = val & 0x7;
46739                 if (type === Pbf.Varint) { while (this.buf[this.pos++] > 0x7f) {} }
46740                 else if (type === Pbf.Bytes) { this.pos = this.readVarint() + this.pos; }
46741                 else if (type === Pbf.Fixed32) { this.pos += 4; }
46742                 else if (type === Pbf.Fixed64) { this.pos += 8; }
46743                 else { throw new Error('Unimplemented type: ' + type); }
46744             },
46745
46746             // === WRITING =================================================================
46747
46748             writeTag: function(tag, type) {
46749                 this.writeVarint((tag << 3) | type);
46750             },
46751
46752             realloc: function(min) {
46753                 var length = this.length || 16;
46754
46755                 while (length < this.pos + min) { length *= 2; }
46756
46757                 if (length !== this.length) {
46758                     var buf = new Uint8Array(length);
46759                     buf.set(this.buf);
46760                     this.buf = buf;
46761                     this.length = length;
46762                 }
46763             },
46764
46765             finish: function() {
46766                 this.length = this.pos;
46767                 this.pos = 0;
46768                 return this.buf.subarray(0, this.length);
46769             },
46770
46771             writeFixed32: function(val) {
46772                 this.realloc(4);
46773                 writeInt32(this.buf, val, this.pos);
46774                 this.pos += 4;
46775             },
46776
46777             writeSFixed32: function(val) {
46778                 this.realloc(4);
46779                 writeInt32(this.buf, val, this.pos);
46780                 this.pos += 4;
46781             },
46782
46783             writeFixed64: function(val) {
46784                 this.realloc(8);
46785                 writeInt32(this.buf, val & -1, this.pos);
46786                 writeInt32(this.buf, Math.floor(val * SHIFT_RIGHT_32), this.pos + 4);
46787                 this.pos += 8;
46788             },
46789
46790             writeSFixed64: function(val) {
46791                 this.realloc(8);
46792                 writeInt32(this.buf, val & -1, this.pos);
46793                 writeInt32(this.buf, Math.floor(val * SHIFT_RIGHT_32), this.pos + 4);
46794                 this.pos += 8;
46795             },
46796
46797             writeVarint: function(val) {
46798                 val = +val || 0;
46799
46800                 if (val > 0xfffffff || val < 0) {
46801                     writeBigVarint(val, this);
46802                     return;
46803                 }
46804
46805                 this.realloc(4);
46806
46807                 this.buf[this.pos++] =           val & 0x7f  | (val > 0x7f ? 0x80 : 0); if (val <= 0x7f) { return; }
46808                 this.buf[this.pos++] = ((val >>>= 7) & 0x7f) | (val > 0x7f ? 0x80 : 0); if (val <= 0x7f) { return; }
46809                 this.buf[this.pos++] = ((val >>>= 7) & 0x7f) | (val > 0x7f ? 0x80 : 0); if (val <= 0x7f) { return; }
46810                 this.buf[this.pos++] =   (val >>> 7) & 0x7f;
46811             },
46812
46813             writeSVarint: function(val) {
46814                 this.writeVarint(val < 0 ? -val * 2 - 1 : val * 2);
46815             },
46816
46817             writeBoolean: function(val) {
46818                 this.writeVarint(Boolean(val));
46819             },
46820
46821             writeString: function(str) {
46822                 str = String(str);
46823                 this.realloc(str.length * 4);
46824
46825                 this.pos++; // reserve 1 byte for short string length
46826
46827                 var startPos = this.pos;
46828                 // write the string directly to the buffer and see how much was written
46829                 this.pos = writeUtf8(this.buf, str, this.pos);
46830                 var len = this.pos - startPos;
46831
46832                 if (len >= 0x80) { makeRoomForExtraLength(startPos, len, this); }
46833
46834                 // finally, write the message length in the reserved place and restore the position
46835                 this.pos = startPos - 1;
46836                 this.writeVarint(len);
46837                 this.pos += len;
46838             },
46839
46840             writeFloat: function(val) {
46841                 this.realloc(4);
46842                 ieee754.write(this.buf, val, this.pos, true, 23, 4);
46843                 this.pos += 4;
46844             },
46845
46846             writeDouble: function(val) {
46847                 this.realloc(8);
46848                 ieee754.write(this.buf, val, this.pos, true, 52, 8);
46849                 this.pos += 8;
46850             },
46851
46852             writeBytes: function(buffer) {
46853                 var len = buffer.length;
46854                 this.writeVarint(len);
46855                 this.realloc(len);
46856                 for (var i = 0; i < len; i++) { this.buf[this.pos++] = buffer[i]; }
46857             },
46858
46859             writeRawMessage: function(fn, obj) {
46860                 this.pos++; // reserve 1 byte for short message length
46861
46862                 // write the message directly to the buffer and see how much was written
46863                 var startPos = this.pos;
46864                 fn(obj, this);
46865                 var len = this.pos - startPos;
46866
46867                 if (len >= 0x80) { makeRoomForExtraLength(startPos, len, this); }
46868
46869                 // finally, write the message length in the reserved place and restore the position
46870                 this.pos = startPos - 1;
46871                 this.writeVarint(len);
46872                 this.pos += len;
46873             },
46874
46875             writeMessage: function(tag, fn, obj) {
46876                 this.writeTag(tag, Pbf.Bytes);
46877                 this.writeRawMessage(fn, obj);
46878             },
46879
46880             writePackedVarint:   function(tag, arr) { if (arr.length) { this.writeMessage(tag, writePackedVarint, arr); }   },
46881             writePackedSVarint:  function(tag, arr) { if (arr.length) { this.writeMessage(tag, writePackedSVarint, arr); }  },
46882             writePackedBoolean:  function(tag, arr) { if (arr.length) { this.writeMessage(tag, writePackedBoolean, arr); }  },
46883             writePackedFloat:    function(tag, arr) { if (arr.length) { this.writeMessage(tag, writePackedFloat, arr); }    },
46884             writePackedDouble:   function(tag, arr) { if (arr.length) { this.writeMessage(tag, writePackedDouble, arr); }   },
46885             writePackedFixed32:  function(tag, arr) { if (arr.length) { this.writeMessage(tag, writePackedFixed32, arr); }  },
46886             writePackedSFixed32: function(tag, arr) { if (arr.length) { this.writeMessage(tag, writePackedSFixed32, arr); } },
46887             writePackedFixed64:  function(tag, arr) { if (arr.length) { this.writeMessage(tag, writePackedFixed64, arr); }  },
46888             writePackedSFixed64: function(tag, arr) { if (arr.length) { this.writeMessage(tag, writePackedSFixed64, arr); } },
46889
46890             writeBytesField: function(tag, buffer) {
46891                 this.writeTag(tag, Pbf.Bytes);
46892                 this.writeBytes(buffer);
46893             },
46894             writeFixed32Field: function(tag, val) {
46895                 this.writeTag(tag, Pbf.Fixed32);
46896                 this.writeFixed32(val);
46897             },
46898             writeSFixed32Field: function(tag, val) {
46899                 this.writeTag(tag, Pbf.Fixed32);
46900                 this.writeSFixed32(val);
46901             },
46902             writeFixed64Field: function(tag, val) {
46903                 this.writeTag(tag, Pbf.Fixed64);
46904                 this.writeFixed64(val);
46905             },
46906             writeSFixed64Field: function(tag, val) {
46907                 this.writeTag(tag, Pbf.Fixed64);
46908                 this.writeSFixed64(val);
46909             },
46910             writeVarintField: function(tag, val) {
46911                 this.writeTag(tag, Pbf.Varint);
46912                 this.writeVarint(val);
46913             },
46914             writeSVarintField: function(tag, val) {
46915                 this.writeTag(tag, Pbf.Varint);
46916                 this.writeSVarint(val);
46917             },
46918             writeStringField: function(tag, str) {
46919                 this.writeTag(tag, Pbf.Bytes);
46920                 this.writeString(str);
46921             },
46922             writeFloatField: function(tag, val) {
46923                 this.writeTag(tag, Pbf.Fixed32);
46924                 this.writeFloat(val);
46925             },
46926             writeDoubleField: function(tag, val) {
46927                 this.writeTag(tag, Pbf.Fixed64);
46928                 this.writeDouble(val);
46929             },
46930             writeBooleanField: function(tag, val) {
46931                 this.writeVarintField(tag, Boolean(val));
46932             }
46933         };
46934
46935         function readVarintRemainder(l, s, p) {
46936             var buf = p.buf,
46937                 h, b;
46938
46939             b = buf[p.pos++]; h  = (b & 0x70) >> 4;  if (b < 0x80) { return toNum(l, h, s); }
46940             b = buf[p.pos++]; h |= (b & 0x7f) << 3;  if (b < 0x80) { return toNum(l, h, s); }
46941             b = buf[p.pos++]; h |= (b & 0x7f) << 10; if (b < 0x80) { return toNum(l, h, s); }
46942             b = buf[p.pos++]; h |= (b & 0x7f) << 17; if (b < 0x80) { return toNum(l, h, s); }
46943             b = buf[p.pos++]; h |= (b & 0x7f) << 24; if (b < 0x80) { return toNum(l, h, s); }
46944             b = buf[p.pos++]; h |= (b & 0x01) << 31; if (b < 0x80) { return toNum(l, h, s); }
46945
46946             throw new Error('Expected varint not more than 10 bytes');
46947         }
46948
46949         function readPackedEnd(pbf) {
46950             return pbf.type === Pbf.Bytes ?
46951                 pbf.readVarint() + pbf.pos : pbf.pos + 1;
46952         }
46953
46954         function toNum(low, high, isSigned) {
46955             if (isSigned) {
46956                 return high * 0x100000000 + (low >>> 0);
46957             }
46958
46959             return ((high >>> 0) * 0x100000000) + (low >>> 0);
46960         }
46961
46962         function writeBigVarint(val, pbf) {
46963             var low, high;
46964
46965             if (val >= 0) {
46966                 low  = (val % 0x100000000) | 0;
46967                 high = (val / 0x100000000) | 0;
46968             } else {
46969                 low  = ~(-val % 0x100000000);
46970                 high = ~(-val / 0x100000000);
46971
46972                 if (low ^ 0xffffffff) {
46973                     low = (low + 1) | 0;
46974                 } else {
46975                     low = 0;
46976                     high = (high + 1) | 0;
46977                 }
46978             }
46979
46980             if (val >= 0x10000000000000000 || val < -0x10000000000000000) {
46981                 throw new Error('Given varint doesn\'t fit into 10 bytes');
46982             }
46983
46984             pbf.realloc(10);
46985
46986             writeBigVarintLow(low, high, pbf);
46987             writeBigVarintHigh(high, pbf);
46988         }
46989
46990         function writeBigVarintLow(low, high, pbf) {
46991             pbf.buf[pbf.pos++] = low & 0x7f | 0x80; low >>>= 7;
46992             pbf.buf[pbf.pos++] = low & 0x7f | 0x80; low >>>= 7;
46993             pbf.buf[pbf.pos++] = low & 0x7f | 0x80; low >>>= 7;
46994             pbf.buf[pbf.pos++] = low & 0x7f | 0x80; low >>>= 7;
46995             pbf.buf[pbf.pos]   = low & 0x7f;
46996         }
46997
46998         function writeBigVarintHigh(high, pbf) {
46999             var lsb = (high & 0x07) << 4;
47000
47001             pbf.buf[pbf.pos++] |= lsb         | ((high >>>= 3) ? 0x80 : 0); if (!high) { return; }
47002             pbf.buf[pbf.pos++]  = high & 0x7f | ((high >>>= 7) ? 0x80 : 0); if (!high) { return; }
47003             pbf.buf[pbf.pos++]  = high & 0x7f | ((high >>>= 7) ? 0x80 : 0); if (!high) { return; }
47004             pbf.buf[pbf.pos++]  = high & 0x7f | ((high >>>= 7) ? 0x80 : 0); if (!high) { return; }
47005             pbf.buf[pbf.pos++]  = high & 0x7f | ((high >>>= 7) ? 0x80 : 0); if (!high) { return; }
47006             pbf.buf[pbf.pos++]  = high & 0x7f;
47007         }
47008
47009         function makeRoomForExtraLength(startPos, len, pbf) {
47010             var extraLen =
47011                 len <= 0x3fff ? 1 :
47012                 len <= 0x1fffff ? 2 :
47013                 len <= 0xfffffff ? 3 : Math.floor(Math.log(len) / (Math.LN2 * 7));
47014
47015             // if 1 byte isn't enough for encoding message length, shift the data to the right
47016             pbf.realloc(extraLen);
47017             for (var i = pbf.pos - 1; i >= startPos; i--) { pbf.buf[i + extraLen] = pbf.buf[i]; }
47018         }
47019
47020         function writePackedVarint(arr, pbf)   { for (var i = 0; i < arr.length; i++) { pbf.writeVarint(arr[i]); }   }
47021         function writePackedSVarint(arr, pbf)  { for (var i = 0; i < arr.length; i++) { pbf.writeSVarint(arr[i]); }  }
47022         function writePackedFloat(arr, pbf)    { for (var i = 0; i < arr.length; i++) { pbf.writeFloat(arr[i]); }    }
47023         function writePackedDouble(arr, pbf)   { for (var i = 0; i < arr.length; i++) { pbf.writeDouble(arr[i]); }   }
47024         function writePackedBoolean(arr, pbf)  { for (var i = 0; i < arr.length; i++) { pbf.writeBoolean(arr[i]); }  }
47025         function writePackedFixed32(arr, pbf)  { for (var i = 0; i < arr.length; i++) { pbf.writeFixed32(arr[i]); }  }
47026         function writePackedSFixed32(arr, pbf) { for (var i = 0; i < arr.length; i++) { pbf.writeSFixed32(arr[i]); } }
47027         function writePackedFixed64(arr, pbf)  { for (var i = 0; i < arr.length; i++) { pbf.writeFixed64(arr[i]); }  }
47028         function writePackedSFixed64(arr, pbf) { for (var i = 0; i < arr.length; i++) { pbf.writeSFixed64(arr[i]); } }
47029
47030         // Buffer code below from https://github.com/feross/buffer, MIT-licensed
47031
47032         function readUInt32(buf, pos) {
47033             return ((buf[pos]) |
47034                 (buf[pos + 1] << 8) |
47035                 (buf[pos + 2] << 16)) +
47036                 (buf[pos + 3] * 0x1000000);
47037         }
47038
47039         function writeInt32(buf, val, pos) {
47040             buf[pos] = val;
47041             buf[pos + 1] = (val >>> 8);
47042             buf[pos + 2] = (val >>> 16);
47043             buf[pos + 3] = (val >>> 24);
47044         }
47045
47046         function readInt32(buf, pos) {
47047             return ((buf[pos]) |
47048                 (buf[pos + 1] << 8) |
47049                 (buf[pos + 2] << 16)) +
47050                 (buf[pos + 3] << 24);
47051         }
47052
47053         function readUtf8(buf, pos, end) {
47054             var str = '';
47055             var i = pos;
47056
47057             while (i < end) {
47058                 var b0 = buf[i];
47059                 var c = null; // codepoint
47060                 var bytesPerSequence =
47061                     b0 > 0xEF ? 4 :
47062                     b0 > 0xDF ? 3 :
47063                     b0 > 0xBF ? 2 : 1;
47064
47065                 if (i + bytesPerSequence > end) { break; }
47066
47067                 var b1, b2, b3;
47068
47069                 if (bytesPerSequence === 1) {
47070                     if (b0 < 0x80) {
47071                         c = b0;
47072                     }
47073                 } else if (bytesPerSequence === 2) {
47074                     b1 = buf[i + 1];
47075                     if ((b1 & 0xC0) === 0x80) {
47076                         c = (b0 & 0x1F) << 0x6 | (b1 & 0x3F);
47077                         if (c <= 0x7F) {
47078                             c = null;
47079                         }
47080                     }
47081                 } else if (bytesPerSequence === 3) {
47082                     b1 = buf[i + 1];
47083                     b2 = buf[i + 2];
47084                     if ((b1 & 0xC0) === 0x80 && (b2 & 0xC0) === 0x80) {
47085                         c = (b0 & 0xF) << 0xC | (b1 & 0x3F) << 0x6 | (b2 & 0x3F);
47086                         if (c <= 0x7FF || (c >= 0xD800 && c <= 0xDFFF)) {
47087                             c = null;
47088                         }
47089                     }
47090                 } else if (bytesPerSequence === 4) {
47091                     b1 = buf[i + 1];
47092                     b2 = buf[i + 2];
47093                     b3 = buf[i + 3];
47094                     if ((b1 & 0xC0) === 0x80 && (b2 & 0xC0) === 0x80 && (b3 & 0xC0) === 0x80) {
47095                         c = (b0 & 0xF) << 0x12 | (b1 & 0x3F) << 0xC | (b2 & 0x3F) << 0x6 | (b3 & 0x3F);
47096                         if (c <= 0xFFFF || c >= 0x110000) {
47097                             c = null;
47098                         }
47099                     }
47100                 }
47101
47102                 if (c === null) {
47103                     c = 0xFFFD;
47104                     bytesPerSequence = 1;
47105
47106                 } else if (c > 0xFFFF) {
47107                     c -= 0x10000;
47108                     str += String.fromCharCode(c >>> 10 & 0x3FF | 0xD800);
47109                     c = 0xDC00 | c & 0x3FF;
47110                 }
47111
47112                 str += String.fromCharCode(c);
47113                 i += bytesPerSequence;
47114             }
47115
47116             return str;
47117         }
47118
47119         function readUtf8TextDecoder(buf, pos, end) {
47120             return utf8TextDecoder.decode(buf.subarray(pos, end));
47121         }
47122
47123         function writeUtf8(buf, str, pos) {
47124             for (var i = 0, c, lead; i < str.length; i++) {
47125                 c = str.charCodeAt(i); // code point
47126
47127                 if (c > 0xD7FF && c < 0xE000) {
47128                     if (lead) {
47129                         if (c < 0xDC00) {
47130                             buf[pos++] = 0xEF;
47131                             buf[pos++] = 0xBF;
47132                             buf[pos++] = 0xBD;
47133                             lead = c;
47134                             continue;
47135                         } else {
47136                             c = lead - 0xD800 << 10 | c - 0xDC00 | 0x10000;
47137                             lead = null;
47138                         }
47139                     } else {
47140                         if (c > 0xDBFF || (i + 1 === str.length)) {
47141                             buf[pos++] = 0xEF;
47142                             buf[pos++] = 0xBF;
47143                             buf[pos++] = 0xBD;
47144                         } else {
47145                             lead = c;
47146                         }
47147                         continue;
47148                     }
47149                 } else if (lead) {
47150                     buf[pos++] = 0xEF;
47151                     buf[pos++] = 0xBF;
47152                     buf[pos++] = 0xBD;
47153                     lead = null;
47154                 }
47155
47156                 if (c < 0x80) {
47157                     buf[pos++] = c;
47158                 } else {
47159                     if (c < 0x800) {
47160                         buf[pos++] = c >> 0x6 | 0xC0;
47161                     } else {
47162                         if (c < 0x10000) {
47163                             buf[pos++] = c >> 0xC | 0xE0;
47164                         } else {
47165                             buf[pos++] = c >> 0x12 | 0xF0;
47166                             buf[pos++] = c >> 0xC & 0x3F | 0x80;
47167                         }
47168                         buf[pos++] = c >> 0x6 & 0x3F | 0x80;
47169                     }
47170                     buf[pos++] = c & 0x3F | 0x80;
47171                 }
47172             }
47173             return pos;
47174         }
47175
47176         var pointGeometry = Point;
47177
47178         /**
47179          * A standalone point geometry with useful accessor, comparison, and
47180          * modification methods.
47181          *
47182          * @class Point
47183          * @param {Number} x the x-coordinate. this could be longitude or screen
47184          * pixels, or any other sort of unit.
47185          * @param {Number} y the y-coordinate. this could be latitude or screen
47186          * pixels, or any other sort of unit.
47187          * @example
47188          * var point = new Point(-77, 38);
47189          */
47190         function Point(x, y) {
47191             this.x = x;
47192             this.y = y;
47193         }
47194
47195         Point.prototype = {
47196
47197             /**
47198              * Clone this point, returning a new point that can be modified
47199              * without affecting the old one.
47200              * @return {Point} the clone
47201              */
47202             clone: function() { return new Point(this.x, this.y); },
47203
47204             /**
47205              * Add this point's x & y coordinates to another point,
47206              * yielding a new point.
47207              * @param {Point} p the other point
47208              * @return {Point} output point
47209              */
47210             add:     function(p) { return this.clone()._add(p); },
47211
47212             /**
47213              * Subtract this point's x & y coordinates to from point,
47214              * yielding a new point.
47215              * @param {Point} p the other point
47216              * @return {Point} output point
47217              */
47218             sub:     function(p) { return this.clone()._sub(p); },
47219
47220             /**
47221              * Multiply this point's x & y coordinates by point,
47222              * yielding a new point.
47223              * @param {Point} p the other point
47224              * @return {Point} output point
47225              */
47226             multByPoint:    function(p) { return this.clone()._multByPoint(p); },
47227
47228             /**
47229              * Divide this point's x & y coordinates by point,
47230              * yielding a new point.
47231              * @param {Point} p the other point
47232              * @return {Point} output point
47233              */
47234             divByPoint:     function(p) { return this.clone()._divByPoint(p); },
47235
47236             /**
47237              * Multiply this point's x & y coordinates by a factor,
47238              * yielding a new point.
47239              * @param {Point} k factor
47240              * @return {Point} output point
47241              */
47242             mult:    function(k) { return this.clone()._mult(k); },
47243
47244             /**
47245              * Divide this point's x & y coordinates by a factor,
47246              * yielding a new point.
47247              * @param {Point} k factor
47248              * @return {Point} output point
47249              */
47250             div:     function(k) { return this.clone()._div(k); },
47251
47252             /**
47253              * Rotate this point around the 0, 0 origin by an angle a,
47254              * given in radians
47255              * @param {Number} a angle to rotate around, in radians
47256              * @return {Point} output point
47257              */
47258             rotate:  function(a) { return this.clone()._rotate(a); },
47259
47260             /**
47261              * Rotate this point around p point by an angle a,
47262              * given in radians
47263              * @param {Number} a angle to rotate around, in radians
47264              * @param {Point} p Point to rotate around
47265              * @return {Point} output point
47266              */
47267             rotateAround:  function(a,p) { return this.clone()._rotateAround(a,p); },
47268
47269             /**
47270              * Multiply this point by a 4x1 transformation matrix
47271              * @param {Array<Number>} m transformation matrix
47272              * @return {Point} output point
47273              */
47274             matMult: function(m) { return this.clone()._matMult(m); },
47275
47276             /**
47277              * Calculate this point but as a unit vector from 0, 0, meaning
47278              * that the distance from the resulting point to the 0, 0
47279              * coordinate will be equal to 1 and the angle from the resulting
47280              * point to the 0, 0 coordinate will be the same as before.
47281              * @return {Point} unit vector point
47282              */
47283             unit:    function() { return this.clone()._unit(); },
47284
47285             /**
47286              * Compute a perpendicular point, where the new y coordinate
47287              * is the old x coordinate and the new x coordinate is the old y
47288              * coordinate multiplied by -1
47289              * @return {Point} perpendicular point
47290              */
47291             perp:    function() { return this.clone()._perp(); },
47292
47293             /**
47294              * Return a version of this point with the x & y coordinates
47295              * rounded to integers.
47296              * @return {Point} rounded point
47297              */
47298             round:   function() { return this.clone()._round(); },
47299
47300             /**
47301              * Return the magitude of this point: this is the Euclidean
47302              * distance from the 0, 0 coordinate to this point's x and y
47303              * coordinates.
47304              * @return {Number} magnitude
47305              */
47306             mag: function() {
47307                 return Math.sqrt(this.x * this.x + this.y * this.y);
47308             },
47309
47310             /**
47311              * Judge whether this point is equal to another point, returning
47312              * true or false.
47313              * @param {Point} other the other point
47314              * @return {boolean} whether the points are equal
47315              */
47316             equals: function(other) {
47317                 return this.x === other.x &&
47318                        this.y === other.y;
47319             },
47320
47321             /**
47322              * Calculate the distance from this point to another point
47323              * @param {Point} p the other point
47324              * @return {Number} distance
47325              */
47326             dist: function(p) {
47327                 return Math.sqrt(this.distSqr(p));
47328             },
47329
47330             /**
47331              * Calculate the distance from this point to another point,
47332              * without the square root step. Useful if you're comparing
47333              * relative distances.
47334              * @param {Point} p the other point
47335              * @return {Number} distance
47336              */
47337             distSqr: function(p) {
47338                 var dx = p.x - this.x,
47339                     dy = p.y - this.y;
47340                 return dx * dx + dy * dy;
47341             },
47342
47343             /**
47344              * Get the angle from the 0, 0 coordinate to this point, in radians
47345              * coordinates.
47346              * @return {Number} angle
47347              */
47348             angle: function() {
47349                 return Math.atan2(this.y, this.x);
47350             },
47351
47352             /**
47353              * Get the angle from this point to another point, in radians
47354              * @param {Point} b the other point
47355              * @return {Number} angle
47356              */
47357             angleTo: function(b) {
47358                 return Math.atan2(this.y - b.y, this.x - b.x);
47359             },
47360
47361             /**
47362              * Get the angle between this point and another point, in radians
47363              * @param {Point} b the other point
47364              * @return {Number} angle
47365              */
47366             angleWith: function(b) {
47367                 return this.angleWithSep(b.x, b.y);
47368             },
47369
47370             /*
47371              * Find the angle of the two vectors, solving the formula for
47372              * the cross product a x b = |a||b|sin(θ) for θ.
47373              * @param {Number} x the x-coordinate
47374              * @param {Number} y the y-coordinate
47375              * @return {Number} the angle in radians
47376              */
47377             angleWithSep: function(x, y) {
47378                 return Math.atan2(
47379                     this.x * y - this.y * x,
47380                     this.x * x + this.y * y);
47381             },
47382
47383             _matMult: function(m) {
47384                 var x = m[0] * this.x + m[1] * this.y,
47385                     y = m[2] * this.x + m[3] * this.y;
47386                 this.x = x;
47387                 this.y = y;
47388                 return this;
47389             },
47390
47391             _add: function(p) {
47392                 this.x += p.x;
47393                 this.y += p.y;
47394                 return this;
47395             },
47396
47397             _sub: function(p) {
47398                 this.x -= p.x;
47399                 this.y -= p.y;
47400                 return this;
47401             },
47402
47403             _mult: function(k) {
47404                 this.x *= k;
47405                 this.y *= k;
47406                 return this;
47407             },
47408
47409             _div: function(k) {
47410                 this.x /= k;
47411                 this.y /= k;
47412                 return this;
47413             },
47414
47415             _multByPoint: function(p) {
47416                 this.x *= p.x;
47417                 this.y *= p.y;
47418                 return this;
47419             },
47420
47421             _divByPoint: function(p) {
47422                 this.x /= p.x;
47423                 this.y /= p.y;
47424                 return this;
47425             },
47426
47427             _unit: function() {
47428                 this._div(this.mag());
47429                 return this;
47430             },
47431
47432             _perp: function() {
47433                 var y = this.y;
47434                 this.y = this.x;
47435                 this.x = -y;
47436                 return this;
47437             },
47438
47439             _rotate: function(angle) {
47440                 var cos = Math.cos(angle),
47441                     sin = Math.sin(angle),
47442                     x = cos * this.x - sin * this.y,
47443                     y = sin * this.x + cos * this.y;
47444                 this.x = x;
47445                 this.y = y;
47446                 return this;
47447             },
47448
47449             _rotateAround: function(angle, p) {
47450                 var cos = Math.cos(angle),
47451                     sin = Math.sin(angle),
47452                     x = p.x + cos * (this.x - p.x) - sin * (this.y - p.y),
47453                     y = p.y + sin * (this.x - p.x) + cos * (this.y - p.y);
47454                 this.x = x;
47455                 this.y = y;
47456                 return this;
47457             },
47458
47459             _round: function() {
47460                 this.x = Math.round(this.x);
47461                 this.y = Math.round(this.y);
47462                 return this;
47463             }
47464         };
47465
47466         /**
47467          * Construct a point from an array if necessary, otherwise if the input
47468          * is already a Point, or an unknown type, return it unchanged
47469          * @param {Array<Number>|Point|*} a any kind of input value
47470          * @return {Point} constructed point, or passed-through value.
47471          * @example
47472          * // this
47473          * var point = Point.convert([0, 1]);
47474          * // is equivalent to
47475          * var point = new Point(0, 1);
47476          */
47477         Point.convert = function (a) {
47478             if (a instanceof Point) {
47479                 return a;
47480             }
47481             if (Array.isArray(a)) {
47482                 return new Point(a[0], a[1]);
47483             }
47484             return a;
47485         };
47486
47487         var vectortilefeature = VectorTileFeature;
47488
47489         function VectorTileFeature(pbf, end, extent, keys, values) {
47490             // Public
47491             this.properties = {};
47492             this.extent = extent;
47493             this.type = 0;
47494
47495             // Private
47496             this._pbf = pbf;
47497             this._geometry = -1;
47498             this._keys = keys;
47499             this._values = values;
47500
47501             pbf.readFields(readFeature, this, end);
47502         }
47503
47504         function readFeature(tag, feature, pbf) {
47505             if (tag == 1) { feature.id = pbf.readVarint(); }
47506             else if (tag == 2) { readTag(pbf, feature); }
47507             else if (tag == 3) { feature.type = pbf.readVarint(); }
47508             else if (tag == 4) { feature._geometry = pbf.pos; }
47509         }
47510
47511         function readTag(pbf, feature) {
47512             var end = pbf.readVarint() + pbf.pos;
47513
47514             while (pbf.pos < end) {
47515                 var key = feature._keys[pbf.readVarint()],
47516                     value = feature._values[pbf.readVarint()];
47517                 feature.properties[key] = value;
47518             }
47519         }
47520
47521         VectorTileFeature.types = ['Unknown', 'Point', 'LineString', 'Polygon'];
47522
47523         VectorTileFeature.prototype.loadGeometry = function() {
47524             var pbf = this._pbf;
47525             pbf.pos = this._geometry;
47526
47527             var end = pbf.readVarint() + pbf.pos,
47528                 cmd = 1,
47529                 length = 0,
47530                 x = 0,
47531                 y = 0,
47532                 lines = [],
47533                 line;
47534
47535             while (pbf.pos < end) {
47536                 if (length <= 0) {
47537                     var cmdLen = pbf.readVarint();
47538                     cmd = cmdLen & 0x7;
47539                     length = cmdLen >> 3;
47540                 }
47541
47542                 length--;
47543
47544                 if (cmd === 1 || cmd === 2) {
47545                     x += pbf.readSVarint();
47546                     y += pbf.readSVarint();
47547
47548                     if (cmd === 1) { // moveTo
47549                         if (line) { lines.push(line); }
47550                         line = [];
47551                     }
47552
47553                     line.push(new pointGeometry(x, y));
47554
47555                 } else if (cmd === 7) {
47556
47557                     // Workaround for https://github.com/mapbox/mapnik-vector-tile/issues/90
47558                     if (line) {
47559                         line.push(line[0].clone()); // closePolygon
47560                     }
47561
47562                 } else {
47563                     throw new Error('unknown command ' + cmd);
47564                 }
47565             }
47566
47567             if (line) { lines.push(line); }
47568
47569             return lines;
47570         };
47571
47572         VectorTileFeature.prototype.bbox = function() {
47573             var pbf = this._pbf;
47574             pbf.pos = this._geometry;
47575
47576             var end = pbf.readVarint() + pbf.pos,
47577                 cmd = 1,
47578                 length = 0,
47579                 x = 0,
47580                 y = 0,
47581                 x1 = Infinity,
47582                 x2 = -Infinity,
47583                 y1 = Infinity,
47584                 y2 = -Infinity;
47585
47586             while (pbf.pos < end) {
47587                 if (length <= 0) {
47588                     var cmdLen = pbf.readVarint();
47589                     cmd = cmdLen & 0x7;
47590                     length = cmdLen >> 3;
47591                 }
47592
47593                 length--;
47594
47595                 if (cmd === 1 || cmd === 2) {
47596                     x += pbf.readSVarint();
47597                     y += pbf.readSVarint();
47598                     if (x < x1) { x1 = x; }
47599                     if (x > x2) { x2 = x; }
47600                     if (y < y1) { y1 = y; }
47601                     if (y > y2) { y2 = y; }
47602
47603                 } else if (cmd !== 7) {
47604                     throw new Error('unknown command ' + cmd);
47605                 }
47606             }
47607
47608             return [x1, y1, x2, y2];
47609         };
47610
47611         VectorTileFeature.prototype.toGeoJSON = function(x, y, z) {
47612             var size = this.extent * Math.pow(2, z),
47613                 x0 = this.extent * x,
47614                 y0 = this.extent * y,
47615                 coords = this.loadGeometry(),
47616                 type = VectorTileFeature.types[this.type],
47617                 i, j;
47618
47619             function project(line) {
47620                 for (var j = 0; j < line.length; j++) {
47621                     var p = line[j], y2 = 180 - (p.y + y0) * 360 / size;
47622                     line[j] = [
47623                         (p.x + x0) * 360 / size - 180,
47624                         360 / Math.PI * Math.atan(Math.exp(y2 * Math.PI / 180)) - 90
47625                     ];
47626                 }
47627             }
47628
47629             switch (this.type) {
47630             case 1:
47631                 var points = [];
47632                 for (i = 0; i < coords.length; i++) {
47633                     points[i] = coords[i][0];
47634                 }
47635                 coords = points;
47636                 project(coords);
47637                 break;
47638
47639             case 2:
47640                 for (i = 0; i < coords.length; i++) {
47641                     project(coords[i]);
47642                 }
47643                 break;
47644
47645             case 3:
47646                 coords = classifyRings(coords);
47647                 for (i = 0; i < coords.length; i++) {
47648                     for (j = 0; j < coords[i].length; j++) {
47649                         project(coords[i][j]);
47650                     }
47651                 }
47652                 break;
47653             }
47654
47655             if (coords.length === 1) {
47656                 coords = coords[0];
47657             } else {
47658                 type = 'Multi' + type;
47659             }
47660
47661             var result = {
47662                 type: "Feature",
47663                 geometry: {
47664                     type: type,
47665                     coordinates: coords
47666                 },
47667                 properties: this.properties
47668             };
47669
47670             if ('id' in this) {
47671                 result.id = this.id;
47672             }
47673
47674             return result;
47675         };
47676
47677         // classifies an array of rings into polygons with outer rings and holes
47678
47679         function classifyRings(rings) {
47680             var len = rings.length;
47681
47682             if (len <= 1) { return [rings]; }
47683
47684             var polygons = [],
47685                 polygon,
47686                 ccw;
47687
47688             for (var i = 0; i < len; i++) {
47689                 var area = signedArea$1(rings[i]);
47690                 if (area === 0) { continue; }
47691
47692                 if (ccw === undefined) { ccw = area < 0; }
47693
47694                 if (ccw === area < 0) {
47695                     if (polygon) { polygons.push(polygon); }
47696                     polygon = [rings[i]];
47697
47698                 } else {
47699                     polygon.push(rings[i]);
47700                 }
47701             }
47702             if (polygon) { polygons.push(polygon); }
47703
47704             return polygons;
47705         }
47706
47707         function signedArea$1(ring) {
47708             var sum = 0;
47709             for (var i = 0, len = ring.length, j = len - 1, p1, p2; i < len; j = i++) {
47710                 p1 = ring[i];
47711                 p2 = ring[j];
47712                 sum += (p2.x - p1.x) * (p1.y + p2.y);
47713             }
47714             return sum;
47715         }
47716
47717         var vectortilelayer = VectorTileLayer;
47718
47719         function VectorTileLayer(pbf, end) {
47720             // Public
47721             this.version = 1;
47722             this.name = null;
47723             this.extent = 4096;
47724             this.length = 0;
47725
47726             // Private
47727             this._pbf = pbf;
47728             this._keys = [];
47729             this._values = [];
47730             this._features = [];
47731
47732             pbf.readFields(readLayer, this, end);
47733
47734             this.length = this._features.length;
47735         }
47736
47737         function readLayer(tag, layer, pbf) {
47738             if (tag === 15) { layer.version = pbf.readVarint(); }
47739             else if (tag === 1) { layer.name = pbf.readString(); }
47740             else if (tag === 5) { layer.extent = pbf.readVarint(); }
47741             else if (tag === 2) { layer._features.push(pbf.pos); }
47742             else if (tag === 3) { layer._keys.push(pbf.readString()); }
47743             else if (tag === 4) { layer._values.push(readValueMessage(pbf)); }
47744         }
47745
47746         function readValueMessage(pbf) {
47747             var value = null,
47748                 end = pbf.readVarint() + pbf.pos;
47749
47750             while (pbf.pos < end) {
47751                 var tag = pbf.readVarint() >> 3;
47752
47753                 value = tag === 1 ? pbf.readString() :
47754                     tag === 2 ? pbf.readFloat() :
47755                     tag === 3 ? pbf.readDouble() :
47756                     tag === 4 ? pbf.readVarint64() :
47757                     tag === 5 ? pbf.readVarint() :
47758                     tag === 6 ? pbf.readSVarint() :
47759                     tag === 7 ? pbf.readBoolean() : null;
47760             }
47761
47762             return value;
47763         }
47764
47765         // return feature `i` from this layer as a `VectorTileFeature`
47766         VectorTileLayer.prototype.feature = function(i) {
47767             if (i < 0 || i >= this._features.length) { throw new Error('feature index out of bounds'); }
47768
47769             this._pbf.pos = this._features[i];
47770
47771             var end = this._pbf.readVarint() + this._pbf.pos;
47772             return new vectortilefeature(this._pbf, end, this.extent, this._keys, this._values);
47773         };
47774
47775         var vectortile = VectorTile;
47776
47777         function VectorTile(pbf, end) {
47778             this.layers = pbf.readFields(readTile, {}, end);
47779         }
47780
47781         function readTile(tag, layers, pbf) {
47782             if (tag === 3) {
47783                 var layer = new vectortilelayer(pbf, pbf.readVarint() + pbf.pos);
47784                 if (layer.length) { layers[layer.name] = layer; }
47785             }
47786         }
47787
47788         var VectorTile$1 = vectortile;
47789         var VectorTileFeature$1 = vectortilefeature;
47790         var VectorTileLayer$1 = vectortilelayer;
47791
47792         var vectorTile = {
47793                 VectorTile: VectorTile$1,
47794                 VectorTileFeature: VectorTileFeature$1,
47795                 VectorTileLayer: VectorTileLayer$1
47796         };
47797
47798         var tiler$7 = utilTiler().tileSize(512).margin(1);
47799         var dispatch$8 = dispatch('loadedData');
47800         var _vtCache;
47801
47802
47803         function abortRequest$7(controller) {
47804             controller.abort();
47805         }
47806
47807
47808         function vtToGeoJSON(data, tile, mergeCache) {
47809             var vectorTile$1 = new vectorTile.VectorTile(new pbf(data));
47810             var layers = Object.keys(vectorTile$1.layers);
47811             if (!Array.isArray(layers)) { layers = [layers]; }
47812
47813             var features = [];
47814             layers.forEach(function(layerID) {
47815                 var layer = vectorTile$1.layers[layerID];
47816                 if (layer) {
47817                     for (var i = 0; i < layer.length; i++) {
47818                         var feature = layer.feature(i).toGeoJSON(tile.xyz[0], tile.xyz[1], tile.xyz[2]);
47819                         var geometry = feature.geometry;
47820
47821                         // Treat all Polygons as MultiPolygons
47822                         if (geometry.type === 'Polygon') {
47823                             geometry.type = 'MultiPolygon';
47824                             geometry.coordinates = [geometry.coordinates];
47825                         }
47826
47827                         // Clip to tile bounds
47828                         if (geometry.type === 'MultiPolygon') {
47829                             var isClipped = false;
47830                             var featureClip = bboxClip_1(feature, tile.extent.rectangle());
47831                             if (!fastDeepEqual(feature.geometry, featureClip.geometry)) {
47832                                 // feature = featureClip;
47833                                 isClipped = true;
47834                             }
47835                             if (!feature.geometry.coordinates.length) { continue; }   // not actually on this tile
47836                             if (!feature.geometry.coordinates[0].length) { continue; }   // not actually on this tile
47837                         }
47838
47839                         // Generate some unique IDs and add some metadata
47840                         var featurehash = utilHashcode(fastJsonStableStringify(feature));
47841                         var propertyhash = utilHashcode(fastJsonStableStringify(feature.properties || {}));
47842                         feature.__layerID__ = layerID.replace(/[^_a-zA-Z0-9\-]/g, '_');
47843                         feature.__featurehash__ = featurehash;
47844                         feature.__propertyhash__ = propertyhash;
47845                         features.push(feature);
47846
47847                         // Clipped Polygons at same zoom with identical properties can get merged
47848                         if (isClipped && geometry.type === 'MultiPolygon') {
47849                             var merged = mergeCache[propertyhash];
47850                             if (merged && merged.length) {
47851                                 var other = merged[0];
47852                                 var coords = union(
47853                                     feature.geometry.coordinates,
47854                                     other.geometry.coordinates
47855                                 );
47856
47857                                 if (!coords || !coords.length) {
47858                                     continue;  // something failed in martinez union
47859                                 }
47860
47861                                 merged.push(feature);
47862                                 for (var j = 0; j < merged.length; j++) {      // all these features get...
47863                                     merged[j].geometry.coordinates = coords;   // same coords
47864                                     merged[j].__featurehash__ = featurehash;   // same hash, so deduplication works
47865                                 }
47866                             } else {
47867                                 mergeCache[propertyhash] = [feature];
47868                             }
47869                         }
47870                     }
47871                 }
47872             });
47873
47874             return features;
47875         }
47876
47877
47878         function loadTile(source, tile) {
47879             if (source.loaded[tile.id] || source.inflight[tile.id]) { return; }
47880
47881             var url = source.template
47882                 .replace('{x}', tile.xyz[0])
47883                 .replace('{y}', tile.xyz[1])
47884                 // TMS-flipped y coordinate
47885                 .replace(/\{[t-]y\}/, Math.pow(2, tile.xyz[2]) - tile.xyz[1] - 1)
47886                 .replace(/\{z(oom)?\}/, tile.xyz[2])
47887                 .replace(/\{switch:([^}]+)\}/, function(s, r) {
47888                     var subdomains = r.split(',');
47889                     return subdomains[(tile.xyz[0] + tile.xyz[1]) % subdomains.length];
47890                 });
47891
47892
47893             var controller = new AbortController();
47894             source.inflight[tile.id] = controller;
47895
47896             fetch(url, { signal: controller.signal })
47897                 .then(function(response) {
47898                     if (!response.ok) {
47899                         throw new Error(response.status + ' ' + response.statusText);
47900                     }
47901                     source.loaded[tile.id] = [];
47902                     delete source.inflight[tile.id];
47903                     return response.arrayBuffer();
47904                 })
47905                 .then(function(data) {
47906                     if (!data) {
47907                         throw new Error('No Data');
47908                     }
47909
47910                     var z = tile.xyz[2];
47911                     if (!source.canMerge[z]) {
47912                         source.canMerge[z] = {};  // initialize mergeCache
47913                     }
47914
47915                     source.loaded[tile.id] = vtToGeoJSON(data, tile, source.canMerge[z]);
47916                     dispatch$8.call('loadedData');
47917                 })
47918                 .catch(function() {
47919                     source.loaded[tile.id] = [];
47920                     delete source.inflight[tile.id];
47921                 });
47922         }
47923
47924
47925         var serviceVectorTile = {
47926
47927             init: function() {
47928                 if (!_vtCache) {
47929                     this.reset();
47930                 }
47931
47932                 this.event = utilRebind(this, dispatch$8, 'on');
47933             },
47934
47935
47936             reset: function() {
47937                 for (var sourceID in _vtCache) {
47938                     var source = _vtCache[sourceID];
47939                     if (source && source.inflight) {
47940                         Object.values(source.inflight).forEach(abortRequest$7);
47941                     }
47942                 }
47943
47944                 _vtCache = {};
47945             },
47946
47947
47948             addSource: function(sourceID, template) {
47949                 _vtCache[sourceID] = { template: template, inflight: {}, loaded: {}, canMerge: {} };
47950                 return _vtCache[sourceID];
47951             },
47952
47953
47954             data: function(sourceID, projection) {
47955                 var source = _vtCache[sourceID];
47956                 if (!source) { return []; }
47957
47958                 var tiles = tiler$7.getTiles(projection);
47959                 var seen = {};
47960                 var results = [];
47961
47962                 for (var i = 0; i < tiles.length; i++) {
47963                     var features = source.loaded[tiles[i].id];
47964                     if (!features || !features.length) { continue; }
47965
47966                     for (var j = 0; j < features.length; j++) {
47967                         var feature = features[j];
47968                         var hash = feature.__featurehash__;
47969                         if (seen[hash]) { continue; }
47970                         seen[hash] = true;
47971
47972                         // return a shallow copy, because the hash may change
47973                         // later if this feature gets merged with another
47974                         results.push(Object.assign({}, feature));  // shallow copy
47975                     }
47976                 }
47977
47978                 return results;
47979             },
47980
47981
47982             loadTiles: function(sourceID, template, projection) {
47983                 var source = _vtCache[sourceID];
47984                 if (!source) {
47985                     source = this.addSource(sourceID, template);
47986                 }
47987
47988                 var tiles = tiler$7.getTiles(projection);
47989
47990                 // abort inflight requests that are no longer needed
47991                 Object.keys(source.inflight).forEach(function(k) {
47992                     var wanted = tiles.find(function(tile) { return k === tile.id; });
47993                     if (!wanted) {
47994                         abortRequest$7(source.inflight[k]);
47995                         delete source.inflight[k];
47996                     }
47997                 });
47998
47999                 tiles.forEach(function(tile) {
48000                     loadTile(source, tile);
48001                 });
48002             },
48003
48004
48005             cache: function() {
48006                 return _vtCache;
48007             }
48008
48009         };
48010
48011         var apibase$5 = 'https://www.wikidata.org/w/api.php?';
48012         var _wikidataCache = {};
48013
48014
48015         var serviceWikidata = {
48016
48017             init: function() {},
48018
48019             reset: function() {
48020                 _wikidataCache = {};
48021             },
48022
48023
48024             // Search for Wikidata items matching the query
48025             itemsForSearchQuery: function(query, callback) {
48026                 if (!query) {
48027                     if (callback) { callback('No query', {}); }
48028                     return;
48029                 }
48030
48031                 var lang = this.languagesToQuery()[0];
48032
48033                 var url = apibase$5 + utilQsString({
48034                     action: 'wbsearchentities',
48035                     format: 'json',
48036                     formatversion: 2,
48037                     search: query,
48038                     type: 'item',
48039                     // the language to search
48040                     language: lang,
48041                     // the langauge for the label and description in the result
48042                     uselang: lang,
48043                     limit: 10,
48044                     origin: '*'
48045                 });
48046
48047                 d3_json(url)
48048                     .then(function(result) {
48049                         if (result && result.error) {
48050                             throw new Error(result.error);
48051                         }
48052                         if (callback) { callback(null, result.search || {}); }
48053                     })
48054                     .catch(function(err) {
48055                         if (callback) { callback(err.message, {}); }
48056                     });
48057             },
48058
48059
48060             // Given a Wikipedia language and article title,
48061             // return an array of corresponding Wikidata entities.
48062             itemsByTitle: function(lang, title, callback) {
48063                 if (!title) {
48064                     if (callback) { callback('No title', {}); }
48065                     return;
48066                 }
48067
48068                 lang = lang || 'en';
48069                 var url = apibase$5 + utilQsString({
48070                     action: 'wbgetentities',
48071                     format: 'json',
48072                     formatversion: 2,
48073                     sites: lang.replace(/-/g, '_') + 'wiki',
48074                     titles: title,
48075                     languages: 'en', // shrink response by filtering to one language
48076                     origin: '*'
48077                 });
48078
48079                 d3_json(url)
48080                     .then(function(result) {
48081                         if (result && result.error) {
48082                             throw new Error(result.error);
48083                         }
48084                         if (callback) { callback(null, result.entities || {}); }
48085                     })
48086                     .catch(function(err) {
48087                         if (callback) { callback(err.message, {}); }
48088                     });
48089             },
48090
48091
48092             languagesToQuery: function() {
48093                 var localeCode = _mainLocalizer.localeCode().toLowerCase();
48094                 // HACK: en-us isn't a wikidata language. We should really be filtering by
48095                 // the languages known to be supported by wikidata.
48096                 if (localeCode === 'en-us') { localeCode = 'en'; }
48097                 return utilArrayUniq([
48098                     localeCode,
48099                     _mainLocalizer.languageCode().toLowerCase(),
48100                     'en'
48101                 ]);
48102             },
48103
48104
48105             entityByQID: function(qid, callback) {
48106                 if (!qid) {
48107                     callback('No qid', {});
48108                     return;
48109                 }
48110                 if (_wikidataCache[qid]) {
48111                     if (callback) { callback(null, _wikidataCache[qid]); }
48112                     return;
48113                 }
48114
48115                 var langs = this.languagesToQuery();
48116                 var url = apibase$5 + utilQsString({
48117                     action: 'wbgetentities',
48118                     format: 'json',
48119                     formatversion: 2,
48120                     ids: qid,
48121                     props: 'labels|descriptions|claims|sitelinks',
48122                     sitefilter: langs.map(function(d) { return d + 'wiki'; }).join('|'),
48123                     languages: langs.join('|'),
48124                     languagefallback: 1,
48125                     origin: '*'
48126                 });
48127
48128                 d3_json(url)
48129                     .then(function(result) {
48130                         if (result && result.error) {
48131                             throw new Error(result.error);
48132                         }
48133                         if (callback) { callback(null, result.entities[qid] || {}); }
48134                     })
48135                     .catch(function(err) {
48136                         if (callback) { callback(err.message, {}); }
48137                     });
48138             },
48139
48140
48141             // Pass `params` object of the form:
48142             // {
48143             //   qid: 'string'      // brand wikidata  (e.g. 'Q37158')
48144             // }
48145             //
48146             // Get an result object used to display tag documentation
48147             // {
48148             //   title:        'string',
48149             //   description:  'string',
48150             //   editURL:      'string',
48151             //   imageURL:     'string',
48152             //   wiki:         { title: 'string', text: 'string', url: 'string' }
48153             // }
48154             //
48155             getDocs: function(params, callback) {
48156                 var langs = this.languagesToQuery();
48157                 this.entityByQID(params.qid, function(err, entity) {
48158                     if (err || !entity) {
48159                         callback(err || 'No entity');
48160                         return;
48161                     }
48162
48163                     var i;
48164                     var description;
48165                     if (entity.descriptions && Object.keys(entity.descriptions).length > 0) {
48166                         description = entity.descriptions[Object.keys(entity.descriptions)[0]].value;
48167                     }
48168
48169                     // prepare result
48170                     var result = {
48171                         title: entity.id,
48172                         description: description,
48173                         editURL: 'https://www.wikidata.org/wiki/' + entity.id
48174                     };
48175
48176                     // add image
48177                     if (entity.claims) {
48178                         var imageroot = 'https://commons.wikimedia.org/w/index.php';
48179                         var props = ['P154','P18'];  // logo image, image
48180                         var prop, image;
48181                         for (i = 0; i < props.length; i++) {
48182                             prop = entity.claims[props[i]];
48183                             if (prop && Object.keys(prop).length > 0) {
48184                                 image = prop[Object.keys(prop)[0]].mainsnak.datavalue.value;
48185                                 if (image) {
48186                                     result.imageURL = imageroot + '?' + utilQsString({
48187                                         title: 'Special:Redirect/file/' + image,
48188                                         width: 400
48189                                     });
48190                                     break;
48191                                 }
48192                             }
48193                         }
48194                     }
48195
48196                     if (entity.sitelinks) {
48197                         var englishLocale = _mainLocalizer.languageCode().toLowerCase() === 'en';
48198
48199                         // must be one of these that we requested..
48200                         for (i = 0; i < langs.length; i++) {   // check each, in order of preference
48201                             var w = langs[i] + 'wiki';
48202                             if (entity.sitelinks[w]) {
48203                                 var title = entity.sitelinks[w].title;
48204                                 var tKey = 'inspector.wiki_reference';
48205                                 if (!englishLocale && langs[i] === 'en') {   // user's locale isn't English but
48206                                     tKey = 'inspector.wiki_en_reference';    // we are sending them to enwiki anyway..
48207                                 }
48208
48209                                 result.wiki = {
48210                                     title: title,
48211                                     text: tKey,
48212                                     url: 'https://' + langs[i] + '.wikipedia.org/wiki/' + title.replace(/ /g, '_')
48213                                 };
48214                                 break;
48215                             }
48216                         }
48217                     }
48218
48219                     callback(null, result);
48220                 });
48221             }
48222
48223         };
48224
48225         var endpoint = 'https://en.wikipedia.org/w/api.php?';
48226
48227         var serviceWikipedia = {
48228
48229             init: function() {},
48230             reset: function() {},
48231
48232
48233             search: function(lang, query, callback) {
48234                 if (!query) {
48235                     if (callback) { callback('No Query', []); }
48236                     return;
48237                 }
48238
48239                 lang = lang || 'en';
48240                 var url = endpoint.replace('en', lang) +
48241                     utilQsString({
48242                         action: 'query',
48243                         list: 'search',
48244                         srlimit: '10',
48245                         srinfo: 'suggestion',
48246                         format: 'json',
48247                         origin: '*',
48248                         srsearch: query
48249                     });
48250
48251                 d3_json(url)
48252                     .then(function(result) {
48253                         if (result && result.error) {
48254                             throw new Error(result.error);
48255                         } else if (!result || !result.query || !result.query.search) {
48256                             throw new Error('No Results');
48257                         }
48258                         if (callback) {
48259                             var titles = result.query.search.map(function(d) { return d.title; });
48260                             callback(null, titles);
48261                         }
48262                     })
48263                     .catch(function(err) {
48264                         if (callback) { callback(err, []); }
48265                     });
48266             },
48267
48268
48269             suggestions: function(lang, query, callback) {
48270                 if (!query) {
48271                     if (callback) { callback('', []); }
48272                     return;
48273                 }
48274
48275                 lang = lang || 'en';
48276                 var url = endpoint.replace('en', lang) +
48277                     utilQsString({
48278                         action: 'opensearch',
48279                         namespace: 0,
48280                         suggest: '',
48281                         format: 'json',
48282                         origin: '*',
48283                         search: query
48284                     });
48285
48286                 d3_json(url)
48287                     .then(function(result) {
48288                         if (result && result.error) {
48289                             throw new Error(result.error);
48290                         } else if (!result || result.length < 2) {
48291                             throw new Error('No Results');
48292                         }
48293                         if (callback) { callback(null, result[1] || []); }
48294                     })
48295                     .catch(function(err) {
48296                         if (callback) { callback(err.message, []); }
48297                     });
48298             },
48299
48300
48301             translations: function(lang, title, callback) {
48302                 if (!title) {
48303                     if (callback) { callback('No Title'); }
48304                     return;
48305                 }
48306
48307                 var url = endpoint.replace('en', lang) +
48308                     utilQsString({
48309                         action: 'query',
48310                         prop: 'langlinks',
48311                         format: 'json',
48312                         origin: '*',
48313                         lllimit: 500,
48314                         titles: title
48315                     });
48316
48317                 d3_json(url)
48318                     .then(function(result) {
48319                         if (result && result.error) {
48320                             throw new Error(result.error);
48321                         } else if (!result || !result.query || !result.query.pages) {
48322                             throw new Error('No Results');
48323                         }
48324                         if (callback) {
48325                             var list = result.query.pages[Object.keys(result.query.pages)[0]];
48326                             var translations = {};
48327                             if (list && list.langlinks) {
48328                                 list.langlinks.forEach(function(d) { translations[d.lang] = d['*']; });
48329                             }
48330                             callback(null, translations);
48331                         }
48332                     })
48333                     .catch(function(err) {
48334                         if (callback) { callback(err.message); }
48335                     });
48336             }
48337
48338         };
48339
48340         var services = {
48341             geocoder: serviceNominatim,
48342             keepRight: serviceKeepRight,
48343             improveOSM: serviceImproveOSM,
48344             osmose: serviceOsmose,
48345             mapillary: serviceMapillary,
48346             openstreetcam: serviceOpenstreetcam,
48347             osm: serviceOsm,
48348             osmWikibase: serviceOsmWikibase,
48349             maprules: serviceMapRules,
48350             streetside: serviceStreetside,
48351             taginfo: serviceTaginfo,
48352             vectorTile: serviceVectorTile,
48353             wikidata: serviceWikidata,
48354             wikipedia: serviceWikipedia
48355         };
48356
48357         function svgIcon(name, svgklass, useklass) {
48358             return function drawIcon(selection) {
48359                 selection.selectAll('svg.icon' + (svgklass ? '.' + svgklass.split(' ')[0] : ''))
48360                     .data([0])
48361                     .enter()
48362                     .append('svg')
48363                     .attr('class', 'icon ' + (svgklass || ''))
48364                     .append('use')
48365                     .attr('xlink:href', name)
48366                     .attr('class', useklass);
48367             };
48368         }
48369
48370         function uiNoteComments() {
48371             var _note;
48372
48373
48374             function noteComments(selection) {
48375                 if (_note.isNew()) { return; } // don't draw .comments-container
48376
48377                 var comments = selection.selectAll('.comments-container')
48378                     .data([0]);
48379
48380                 comments = comments.enter()
48381                     .append('div')
48382                     .attr('class', 'comments-container')
48383                     .merge(comments);
48384
48385                 var commentEnter = comments.selectAll('.comment')
48386                     .data(_note.comments)
48387                     .enter()
48388                     .append('div')
48389                     .attr('class', 'comment');
48390
48391                 commentEnter
48392                     .append('div')
48393                     .attr('class', function(d) { return 'comment-avatar user-' + d.uid; })
48394                     .call(svgIcon('#iD-icon-avatar', 'comment-avatar-icon'));
48395
48396                 var mainEnter = commentEnter
48397                     .append('div')
48398                     .attr('class', 'comment-main');
48399
48400                 var metadataEnter = mainEnter
48401                     .append('div')
48402                     .attr('class', 'comment-metadata');
48403
48404                 metadataEnter
48405                     .append('div')
48406                     .attr('class', 'comment-author')
48407                     .each(function(d) {
48408                         var selection = select(this);
48409                         var osm = services.osm;
48410                         if (osm && d.user) {
48411                             selection = selection
48412                                 .append('a')
48413                                 .attr('class', 'comment-author-link')
48414                                 .attr('href', osm.userURL(d.user))
48415                                 .attr('tabindex', -1)
48416                                 .attr('target', '_blank');
48417                         }
48418                         selection
48419                             .text(function(d) { return d.user || _t('note.anonymous'); });
48420                     });
48421
48422                 metadataEnter
48423                     .append('div')
48424                     .attr('class', 'comment-date')
48425                     .text(function(d) {
48426                         return _t('note.status.' + d.action, { when: localeDateString(d.date) });
48427                     });
48428
48429                 mainEnter
48430                     .append('div')
48431                     .attr('class', 'comment-text')
48432                     .html(function(d) { return d.html; });
48433
48434                 comments
48435                     .call(replaceAvatars);
48436             }
48437
48438
48439             function replaceAvatars(selection) {
48440                 var showThirdPartyIcons = corePreferences('preferences.privacy.thirdpartyicons') || 'true';
48441                 var osm = services.osm;
48442                 if (showThirdPartyIcons !== 'true' || !osm) { return; }
48443
48444                 var uids = {};  // gather uids in the comment thread
48445                 _note.comments.forEach(function(d) {
48446                     if (d.uid) { uids[d.uid] = true; }
48447                 });
48448
48449                 Object.keys(uids).forEach(function(uid) {
48450                     osm.loadUser(uid, function(err, user) {
48451                         if (!user || !user.image_url) { return; }
48452
48453                         selection.selectAll('.comment-avatar.user-' + uid)
48454                             .html('')
48455                             .append('img')
48456                             .attr('class', 'icon comment-avatar-icon')
48457                             .attr('src', user.image_url)
48458                             .attr('alt', user.display_name);
48459                     });
48460                 });
48461             }
48462
48463
48464             function localeDateString(s) {
48465                 if (!s) { return null; }
48466                 var options = { day: 'numeric', month: 'short', year: 'numeric' };
48467                 s = s.replace(/-/g, '/'); // fix browser-specific Date() issues
48468                 var d = new Date(s);
48469                 if (isNaN(d.getTime())) { return null; }
48470                 return d.toLocaleDateString(_mainLocalizer.localeCode(), options);
48471             }
48472
48473
48474             noteComments.note = function(val) {
48475                 if (!arguments.length) { return _note; }
48476                 _note = val;
48477                 return noteComments;
48478             };
48479
48480
48481             return noteComments;
48482         }
48483
48484         function uiNoteHeader() {
48485             var _note;
48486
48487
48488             function noteHeader(selection) {
48489                 var header = selection.selectAll('.note-header')
48490                     .data(
48491                         (_note ? [_note] : []),
48492                         function(d) { return d.status + d.id; }
48493                     );
48494
48495                 header.exit()
48496                     .remove();
48497
48498                 var headerEnter = header.enter()
48499                     .append('div')
48500                     .attr('class', 'note-header');
48501
48502                 var iconEnter = headerEnter
48503                     .append('div')
48504                     .attr('class', function(d) { return 'note-header-icon ' + d.status; })
48505                     .classed('new', function(d) { return d.id < 0; });
48506
48507                 iconEnter
48508                     .append('div')
48509                     .attr('class', 'preset-icon-28')
48510                     .call(svgIcon('#iD-icon-note', 'note-fill'));
48511
48512                 iconEnter.each(function(d) {
48513                     var statusIcon = '#iD-icon-' + (d.id < 0 ? 'plus' : (d.status === 'open' ? 'close' : 'apply'));
48514                     iconEnter
48515                         .append('div')
48516                         .attr('class', 'note-icon-annotation')
48517                         .call(svgIcon(statusIcon, 'icon-annotation'));
48518                 });
48519
48520                 headerEnter
48521                     .append('div')
48522                     .attr('class', 'note-header-label')
48523                     .text(function(d) {
48524                         if (_note.isNew()) { return _t('note.new'); }
48525                         return _t('note.note') + ' ' + d.id + ' ' +
48526                             (d.status === 'closed' ? _t('note.closed') : '');
48527                     });
48528             }
48529
48530
48531             noteHeader.note = function(val) {
48532                 if (!arguments.length) { return _note; }
48533                 _note = val;
48534                 return noteHeader;
48535             };
48536
48537
48538             return noteHeader;
48539         }
48540
48541         function uiNoteReport() {
48542             var _note;
48543
48544             function noteReport(selection) {
48545                 var url;
48546                 if (services.osm && (_note instanceof osmNote) && (!_note.isNew())) {
48547                     url = services.osm.noteReportURL(_note);
48548                 }
48549
48550                 var link = selection.selectAll('.note-report')
48551                     .data(url ? [url] : []);
48552
48553                 // exit
48554                 link.exit()
48555                     .remove();
48556
48557                 // enter
48558                 var linkEnter = link.enter()
48559                     .append('a')
48560                     .attr('class', 'note-report')
48561                     .attr('target', '_blank')
48562                     .attr('href', function(d) { return d; })
48563                     .call(svgIcon('#iD-icon-out-link', 'inline'));
48564
48565                 linkEnter
48566                     .append('span')
48567                     .text(_t('note.report'));
48568             }
48569
48570
48571             noteReport.note = function(val) {
48572                 if (!arguments.length) { return _note; }
48573                 _note = val;
48574                 return noteReport;
48575             };
48576
48577             return noteReport;
48578         }
48579
48580         function uiViewOnOSM(context) {
48581             var _what;   // an osmEntity or osmNote
48582
48583
48584             function viewOnOSM(selection) {
48585                 var url;
48586                 if (_what instanceof osmEntity) {
48587                     url = context.connection().entityURL(_what);
48588                 } else if (_what instanceof osmNote) {
48589                     url = context.connection().noteURL(_what);
48590                 }
48591
48592                 var data = ((!_what || _what.isNew()) ? [] : [_what]);
48593                 var link = selection.selectAll('.view-on-osm')
48594                     .data(data, function(d) { return d.id; });
48595
48596                 // exit
48597                 link.exit()
48598                     .remove();
48599
48600                 // enter
48601                 var linkEnter = link.enter()
48602                     .append('a')
48603                     .attr('class', 'view-on-osm')
48604                     .attr('target', '_blank')
48605                     .attr('href', url)
48606                     .call(svgIcon('#iD-icon-out-link', 'inline'));
48607
48608                 linkEnter
48609                     .append('span')
48610                     .text(_t('inspector.view_on_osm'));
48611             }
48612
48613
48614             viewOnOSM.what = function(_) {
48615                 if (!arguments.length) { return _what; }
48616                 _what = _;
48617                 return viewOnOSM;
48618             };
48619
48620             return viewOnOSM;
48621         }
48622
48623         function uiNoteEditor(context) {
48624             var dispatch$1 = dispatch('change');
48625             var noteComments = uiNoteComments();
48626             var noteHeader = uiNoteHeader();
48627
48628             // var formFields = uiFormFields(context);
48629
48630             var _note;
48631             var _newNote;
48632             // var _fieldsArr;
48633
48634
48635             function noteEditor(selection) {
48636
48637                 var header = selection.selectAll('.header')
48638                     .data([0]);
48639
48640                 var headerEnter = header.enter()
48641                     .append('div')
48642                     .attr('class', 'header fillL');
48643
48644                 headerEnter
48645                     .append('button')
48646                     .attr('class', 'close')
48647                     .on('click', function() {
48648                         context.enter(modeBrowse(context));
48649                     })
48650                     .call(svgIcon('#iD-icon-close'));
48651
48652                 headerEnter
48653                     .append('h3')
48654                     .text(_t('note.title'));
48655
48656
48657                 var body = selection.selectAll('.body')
48658                     .data([0]);
48659
48660                 body = body.enter()
48661                     .append('div')
48662                     .attr('class', 'body')
48663                     .merge(body);
48664
48665                 var editor = body.selectAll('.note-editor')
48666                     .data([0]);
48667
48668                 editor.enter()
48669                     .append('div')
48670                     .attr('class', 'modal-section note-editor')
48671                     .merge(editor)
48672                     .call(noteHeader.note(_note))
48673                     .call(noteComments.note(_note))
48674                     .call(noteSaveSection);
48675
48676                 var footer = selection.selectAll('.footer')
48677                     .data([0]);
48678
48679                 footer.enter()
48680                     .append('div')
48681                     .attr('class', 'footer')
48682                     .merge(footer)
48683                     .call(uiViewOnOSM(context).what(_note))
48684                     .call(uiNoteReport().note(_note));
48685
48686
48687                 // rerender the note editor on any auth change
48688                 var osm = services.osm;
48689                 if (osm) {
48690                     osm.on('change.note-save', function() {
48691                         selection.call(noteEditor);
48692                     });
48693                 }
48694             }
48695
48696
48697             function noteSaveSection(selection) {
48698                 var isSelected = (_note && _note.id === context.selectedNoteID());
48699                 var noteSave = selection.selectAll('.note-save')
48700                     .data((isSelected ? [_note] : []), function(d) { return d.status + d.id; });
48701
48702                 // exit
48703                 noteSave.exit()
48704                     .remove();
48705
48706                 // enter
48707                 var noteSaveEnter = noteSave.enter()
48708                     .append('div')
48709                     .attr('class', 'note-save save-section cf');
48710
48711                 // // if new note, show categories to pick from
48712                 // if (_note.isNew()) {
48713                 //     var presets = presetManager;
48714
48715                 //     // NOTE: this key isn't a age and therefore there is no documentation (yet)
48716                 //     _fieldsArr = [
48717                 //         uiField(context, presets.field('category'), null, { show: true, revert: false }),
48718                 //     ];
48719
48720                 //     _fieldsArr.forEach(function(field) {
48721                 //         field
48722                 //             .on('change', changeCategory);
48723                 //     });
48724
48725                 //     noteSaveEnter
48726                 //         .append('div')
48727                 //         .attr('class', 'note-category')
48728                 //         .call(formFields.fieldsArr(_fieldsArr));
48729                 // }
48730
48731                 // function changeCategory() {
48732                 //     // NOTE: perhaps there is a better way to get value
48733                 //     var val = context.container().select('input[name=\'category\']:checked').property('__data__') || undefined;
48734
48735                 //     // store the unsaved category with the note itself
48736                 //     _note = _note.update({ newCategory: val });
48737                 //     var osm = services.osm;
48738                 //     if (osm) {
48739                 //         osm.replaceNote(_note);  // update note cache
48740                 //     }
48741                 //     noteSave
48742                 //         .call(noteSaveButtons);
48743                 // }
48744
48745                 noteSaveEnter
48746                     .append('h4')
48747                     .attr('class', '.note-save-header')
48748                     .text(function() {
48749                         return _note.isNew() ? _t('note.newDescription') : _t('note.newComment');
48750                     });
48751
48752                 var commentTextarea = noteSaveEnter
48753                     .append('textarea')
48754                     .attr('class', 'new-comment-input')
48755                     .attr('placeholder', _t('note.inputPlaceholder'))
48756                     .attr('maxlength', 1000)
48757                     .property('value', function(d) { return d.newComment; })
48758                     .call(utilNoAuto)
48759                     .on('keydown.note-input', keydown)
48760                     .on('input.note-input', changeInput)
48761                     .on('blur.note-input', changeInput);
48762
48763                 if (_newNote) {
48764                     // autofocus the comment field for new notes
48765                     commentTextarea.node().focus();
48766                 }
48767
48768                 // update
48769                 noteSave = noteSaveEnter
48770                     .merge(noteSave)
48771                     .call(userDetails)
48772                     .call(noteSaveButtons);
48773
48774
48775                 // fast submit if user presses cmd+enter
48776                 function keydown() {
48777                     if (!(event.keyCode === 13 && event.metaKey)) { return; }
48778
48779                     var osm = services.osm;
48780                     if (!osm) { return; }
48781
48782                     var hasAuth = osm.authenticated();
48783                     if (!hasAuth) { return; }
48784
48785                     if (!_note.newComment) { return; }
48786
48787                     event.preventDefault();
48788
48789                     select(this)
48790                         .on('keydown.note-input', null);
48791
48792                     // focus on button and submit
48793                     window.setTimeout(function() {
48794                         if (_note.isNew()) {
48795                             noteSave.selectAll('.save-button').node().focus();
48796                             clickSave(_note);
48797                         } else  {
48798                             noteSave.selectAll('.comment-button').node().focus();
48799                             clickComment(_note);
48800                         }
48801                     }, 10);
48802                 }
48803
48804
48805                 function changeInput() {
48806                     var input = select(this);
48807                     var val = input.property('value').trim() || undefined;
48808
48809                     // store the unsaved comment with the note itself
48810                     _note = _note.update({ newComment: val });
48811
48812                     var osm = services.osm;
48813                     if (osm) {
48814                         osm.replaceNote(_note);  // update note cache
48815                     }
48816
48817                     noteSave
48818                         .call(noteSaveButtons);
48819                 }
48820             }
48821
48822
48823             function userDetails(selection) {
48824                 var detailSection = selection.selectAll('.detail-section')
48825                     .data([0]);
48826
48827                 detailSection = detailSection.enter()
48828                     .append('div')
48829                     .attr('class', 'detail-section')
48830                     .merge(detailSection);
48831
48832                 var osm = services.osm;
48833                 if (!osm) { return; }
48834
48835                 // Add warning if user is not logged in
48836                 var hasAuth = osm.authenticated();
48837                 var authWarning = detailSection.selectAll('.auth-warning')
48838                     .data(hasAuth ? [] : [0]);
48839
48840                 authWarning.exit()
48841                     .transition()
48842                     .duration(200)
48843                     .style('opacity', 0)
48844                     .remove();
48845
48846                 var authEnter = authWarning.enter()
48847                     .insert('div', '.tag-reference-body')
48848                     .attr('class', 'field-warning auth-warning')
48849                     .style('opacity', 0);
48850
48851                 authEnter
48852                     .call(svgIcon('#iD-icon-alert', 'inline'));
48853
48854                 authEnter
48855                     .append('span')
48856                     .text(_t('note.login'));
48857
48858                 authEnter
48859                     .append('a')
48860                     .attr('target', '_blank')
48861                     .call(svgIcon('#iD-icon-out-link', 'inline'))
48862                     .append('span')
48863                     .text(_t('login'))
48864                     .on('click.note-login', function() {
48865                         event.preventDefault();
48866                         osm.authenticate();
48867                     });
48868
48869                 authEnter
48870                     .transition()
48871                     .duration(200)
48872                     .style('opacity', 1);
48873
48874
48875                 var prose = detailSection.selectAll('.note-save-prose')
48876                     .data(hasAuth ? [0] : []);
48877
48878                 prose.exit()
48879                     .remove();
48880
48881                 prose = prose.enter()
48882                     .append('p')
48883                     .attr('class', 'note-save-prose')
48884                     .text(_t('note.upload_explanation'))
48885                     .merge(prose);
48886
48887                 osm.userDetails(function(err, user) {
48888                     if (err) { return; }
48889
48890                     var userLink = select(document.createElement('div'));
48891
48892                     if (user.image_url) {
48893                         userLink
48894                             .append('img')
48895                             .attr('src', user.image_url)
48896                             .attr('class', 'icon pre-text user-icon');
48897                     }
48898
48899                     userLink
48900                         .append('a')
48901                         .attr('class', 'user-info')
48902                         .text(user.display_name)
48903                         .attr('href', osm.userURL(user.display_name))
48904                         .attr('tabindex', -1)
48905                         .attr('target', '_blank');
48906
48907                     prose
48908                         .html(_t('note.upload_explanation_with_user', { user: userLink.html() }));
48909                 });
48910             }
48911
48912
48913             function noteSaveButtons(selection) {
48914                 var osm = services.osm;
48915                 var hasAuth = osm && osm.authenticated();
48916
48917                 var isSelected = (_note && _note.id === context.selectedNoteID());
48918                 var buttonSection = selection.selectAll('.buttons')
48919                     .data((isSelected ? [_note] : []), function(d) { return d.status + d.id; });
48920
48921                 // exit
48922                 buttonSection.exit()
48923                     .remove();
48924
48925                 // enter
48926                 var buttonEnter = buttonSection.enter()
48927                     .append('div')
48928                     .attr('class', 'buttons');
48929
48930                 if (_note.isNew()) {
48931                     buttonEnter
48932                         .append('button')
48933                         .attr('class', 'button cancel-button secondary-action')
48934                         .text(_t('confirm.cancel'));
48935
48936                     buttonEnter
48937                         .append('button')
48938                         .attr('class', 'button save-button action')
48939                         .text(_t('note.save'));
48940
48941                 } else {
48942                     buttonEnter
48943                         .append('button')
48944                         .attr('class', 'button status-button action');
48945
48946                     buttonEnter
48947                         .append('button')
48948                         .attr('class', 'button comment-button action')
48949                         .text(_t('note.comment'));
48950                 }
48951
48952
48953                 // update
48954                 buttonSection = buttonSection
48955                     .merge(buttonEnter);
48956
48957                 buttonSection.select('.cancel-button')   // select and propagate data
48958                     .on('click.cancel', clickCancel);
48959
48960                 buttonSection.select('.save-button')     // select and propagate data
48961                     .attr('disabled', isSaveDisabled)
48962                     .on('click.save', clickSave);
48963
48964                 buttonSection.select('.status-button')   // select and propagate data
48965                     .attr('disabled', (hasAuth ? null : true))
48966                     .text(function(d) {
48967                         var action = (d.status === 'open' ? 'close' : 'open');
48968                         var andComment = (d.newComment ? '_comment' : '');
48969                         return _t('note.' + action + andComment);
48970                     })
48971                     .on('click.status', clickStatus);
48972
48973                 buttonSection.select('.comment-button')   // select and propagate data
48974                     .attr('disabled', isSaveDisabled)
48975                     .on('click.comment', clickComment);
48976
48977
48978                 function isSaveDisabled(d) {
48979                     return (hasAuth && d.status === 'open' && d.newComment) ? null : true;
48980                 }
48981             }
48982
48983
48984
48985             function clickCancel(d) {
48986                 this.blur();    // avoid keeping focus on the button - #4641
48987                 var osm = services.osm;
48988                 if (osm) {
48989                     osm.removeNote(d);
48990                 }
48991                 context.enter(modeBrowse(context));
48992                 dispatch$1.call('change');
48993             }
48994
48995
48996             function clickSave(d) {
48997                 this.blur();    // avoid keeping focus on the button - #4641
48998                 var osm = services.osm;
48999                 if (osm) {
49000                     osm.postNoteCreate(d, function(err, note) {
49001                         dispatch$1.call('change', note);
49002                     });
49003                 }
49004             }
49005
49006
49007             function clickStatus(d) {
49008                 this.blur();    // avoid keeping focus on the button - #4641
49009                 var osm = services.osm;
49010                 if (osm) {
49011                     var setStatus = (d.status === 'open' ? 'closed' : 'open');
49012                     osm.postNoteUpdate(d, setStatus, function(err, note) {
49013                         dispatch$1.call('change', note);
49014                     });
49015                 }
49016             }
49017
49018             function clickComment(d) {
49019                 this.blur();    // avoid keeping focus on the button - #4641
49020                 var osm = services.osm;
49021                 if (osm) {
49022                     osm.postNoteUpdate(d, d.status, function(err, note) {
49023                         dispatch$1.call('change', note);
49024                     });
49025                 }
49026             }
49027
49028
49029             noteEditor.note = function(val) {
49030                 if (!arguments.length) { return _note; }
49031                 _note = val;
49032                 return noteEditor;
49033             };
49034
49035             noteEditor.newNote = function(val) {
49036                 if (!arguments.length) { return _newNote; }
49037                 _newNote = val;
49038                 return noteEditor;
49039             };
49040
49041
49042             return utilRebind(noteEditor, dispatch$1, 'on');
49043         }
49044
49045         function modeSelectNote(context, selectedNoteID) {
49046             var mode = {
49047                 id: 'select-note',
49048                 button: 'browse'
49049             };
49050
49051             var _keybinding = utilKeybinding('select-note');
49052             var _noteEditor = uiNoteEditor(context)
49053                 .on('change', function() {
49054                     context.map().pan([0,0]);  // trigger a redraw
49055                     var note = checkSelectedID();
49056                     if (!note) { return; }
49057                     context.ui().sidebar
49058                         .show(_noteEditor.note(note));
49059                 });
49060
49061             var _behaviors = [
49062                 behaviorBreathe(),
49063                 behaviorHover(context),
49064                 behaviorSelect(context),
49065                 behaviorLasso(context),
49066                 modeDragNode(context).behavior,
49067                 modeDragNote(context).behavior
49068             ];
49069
49070             var _newFeature = false;
49071
49072
49073             function checkSelectedID() {
49074                 if (!services.osm) { return; }
49075                 var note = services.osm.getNote(selectedNoteID);
49076                 if (!note) {
49077                     context.enter(modeBrowse(context));
49078                 }
49079                 return note;
49080             }
49081
49082
49083             // class the note as selected, or return to browse mode if the note is gone
49084             function selectNote(drawn) {
49085                 if (!checkSelectedID()) { return; }
49086
49087                 var selection = context.surface().selectAll('.layer-notes .note-' + selectedNoteID);
49088
49089                 if (selection.empty()) {
49090                     // Return to browse mode if selected DOM elements have
49091                     // disappeared because the user moved them out of view..
49092                     var source = event && event.type === 'zoom' && event.sourceEvent;
49093                     if (drawn && source && (source.type === 'pointermove' || source.type === 'mousemove' || source.type === 'touchmove')) {
49094                         context.enter(modeBrowse(context));
49095                     }
49096
49097                 } else {
49098                     selection
49099                         .classed('selected', true);
49100
49101                     context.selectedNoteID(selectedNoteID);
49102                 }
49103             }
49104
49105
49106             function esc() {
49107                 if (context.container().select('.combobox').size()) { return; }
49108                 context.enter(modeBrowse(context));
49109             }
49110
49111
49112             mode.zoomToSelected = function() {
49113                 if (!services.osm) { return; }
49114                 var note = services.osm.getNote(selectedNoteID);
49115                 if (note) {
49116                     context.map().centerZoomEase(note.loc, 20);
49117                 }
49118             };
49119
49120
49121             mode.newFeature = function(val) {
49122                 if (!arguments.length) { return _newFeature; }
49123                 _newFeature = val;
49124                 return mode;
49125             };
49126
49127
49128             mode.enter = function() {
49129                 var note = checkSelectedID();
49130                 if (!note) { return; }
49131
49132                 _behaviors.forEach(context.install);
49133
49134                 _keybinding
49135                     .on(_t('inspector.zoom_to.key'), mode.zoomToSelected)
49136                     .on('⎋', esc, true);
49137
49138                 select(document)
49139                     .call(_keybinding);
49140
49141                 selectNote();
49142
49143                 var sidebar = context.ui().sidebar;
49144                 sidebar.show(_noteEditor.note(note).newNote(_newFeature));
49145
49146                 // expand the sidebar, avoid obscuring the note if needed
49147                 sidebar.expand(sidebar.intersects(note.extent()));
49148
49149                 context.map()
49150                     .on('drawn.select', selectNote);
49151             };
49152
49153
49154             mode.exit = function() {
49155                 _behaviors.forEach(context.uninstall);
49156
49157                 select(document)
49158                     .call(_keybinding.unbind);
49159
49160                 context.surface()
49161                     .selectAll('.layer-notes .selected')
49162                     .classed('selected hover', false);
49163
49164                 context.map()
49165                     .on('drawn.select', null);
49166
49167                 context.ui().sidebar
49168                     .hide();
49169
49170                 context.selectedNoteID(null);
49171             };
49172
49173
49174             return mode;
49175         }
49176
49177         function modeDragNote(context) {
49178             var mode = {
49179                 id: 'drag-note',
49180                 button: 'browse'
49181             };
49182
49183             var edit = behaviorEdit(context);
49184
49185             var _nudgeInterval;
49186             var _lastLoc;
49187             var _note;    // most current note.. dragged note may have stale datum.
49188
49189
49190             function startNudge(nudge) {
49191                 if (_nudgeInterval) { window.clearInterval(_nudgeInterval); }
49192                 _nudgeInterval = window.setInterval(function() {
49193                     context.map().pan(nudge);
49194                     doMove(nudge);
49195                 }, 50);
49196             }
49197
49198
49199             function stopNudge() {
49200                 if (_nudgeInterval) {
49201                     window.clearInterval(_nudgeInterval);
49202                     _nudgeInterval = null;
49203                 }
49204             }
49205
49206
49207             function origin(note) {
49208                 return context.projection(note.loc);
49209             }
49210
49211
49212             function start(note) {
49213                 _note = note;
49214                 var osm = services.osm;
49215                 if (osm) {
49216                     // Get latest note from cache.. The marker may have a stale datum bound to it
49217                     // and dragging it around can sometimes delete the users note comment.
49218                     _note = osm.getNote(_note.id);
49219                 }
49220
49221                 context.surface().selectAll('.note-' + _note.id)
49222                     .classed('active', true);
49223
49224                 context.perform(actionNoop());
49225                 context.enter(mode);
49226                 context.selectedNoteID(_note.id);
49227             }
49228
49229
49230             function move() {
49231                 event.sourceEvent.stopPropagation();
49232                 _lastLoc = context.projection.invert(event.point);
49233
49234                 doMove();
49235                 var nudge = geoViewportEdge(event.point, context.map().dimensions());
49236                 if (nudge) {
49237                     startNudge(nudge);
49238                 } else {
49239                     stopNudge();
49240                 }
49241             }
49242
49243
49244             function doMove(nudge) {
49245                 nudge = nudge || [0, 0];
49246
49247                 var currPoint = (event && event.point) || context.projection(_lastLoc);
49248                 var currMouse = geoVecSubtract(currPoint, nudge);
49249                 var loc = context.projection.invert(currMouse);
49250
49251                 _note = _note.move(loc);
49252
49253                 var osm = services.osm;
49254                 if (osm) {
49255                     osm.replaceNote(_note);  // update note cache
49256                 }
49257
49258                 context.replace(actionNoop());   // trigger redraw
49259             }
49260
49261
49262             function end() {
49263                 context.replace(actionNoop());   // trigger redraw
49264
49265                 context
49266                     .selectedNoteID(_note.id)
49267                     .enter(modeSelectNote(context, _note.id));
49268             }
49269
49270
49271             var drag = behaviorDrag()
49272                 .selector('.layer-touch.markers .target.note.new')
49273                 .surface(context.container().select('.main-map').node())
49274                 .origin(origin)
49275                 .on('start', start)
49276                 .on('move', move)
49277                 .on('end', end);
49278
49279
49280             mode.enter = function() {
49281                 context.install(edit);
49282             };
49283
49284
49285             mode.exit = function() {
49286                 context.ui().sidebar.hover.cancel();
49287                 context.uninstall(edit);
49288
49289                 context.surface()
49290                     .selectAll('.active')
49291                     .classed('active', false);
49292
49293                 stopNudge();
49294             };
49295
49296             mode.behavior = drag;
49297
49298             return mode;
49299         }
49300
49301         function uiDataHeader() {
49302             var _datum;
49303
49304
49305             function dataHeader(selection) {
49306                 var header = selection.selectAll('.data-header')
49307                     .data(
49308                         (_datum ? [_datum] : []),
49309                         function(d) { return d.__featurehash__; }
49310                     );
49311
49312                 header.exit()
49313                     .remove();
49314
49315                 var headerEnter = header.enter()
49316                     .append('div')
49317                     .attr('class', 'data-header');
49318
49319                 var iconEnter = headerEnter
49320                     .append('div')
49321                     .attr('class', 'data-header-icon');
49322
49323                 iconEnter
49324                     .append('div')
49325                     .attr('class', 'preset-icon-28')
49326                     .call(svgIcon('#iD-icon-data', 'note-fill'));
49327
49328                 headerEnter
49329                     .append('div')
49330                     .attr('class', 'data-header-label')
49331                     .text(_t('map_data.layers.custom.title'));
49332             }
49333
49334
49335             dataHeader.datum = function(val) {
49336                 if (!arguments.length) { return _datum; }
49337                 _datum = val;
49338                 return this;
49339             };
49340
49341
49342             return dataHeader;
49343         }
49344
49345         // This code assumes that the combobox values will not have duplicate entries.
49346         // It is keyed on the `value` of the entry. Data should be an array of objects like:
49347         //   [{
49348         //       value:  'display text',  // required
49349         //       title:  'hover text'     // optional
49350         //   }, ...]
49351
49352         var _comboHideTimerID;
49353
49354         function uiCombobox(context, klass) {
49355             var dispatch$1 = dispatch('accept', 'cancel');
49356             var container = context.container();
49357
49358             var _suggestions = [];
49359             var _data = [];
49360             var _fetched = {};
49361             var _selected = null;
49362             var _canAutocomplete = true;
49363             var _caseSensitive = false;
49364             var _cancelFetch = false;
49365             var _minItems = 2;
49366             var _tDown = 0;
49367             var _mouseEnterHandler, _mouseLeaveHandler;
49368
49369             var _fetcher = function(val, cb) {
49370                 cb(_data.filter(function(d) {
49371                     var terms = d.terms || [];
49372                     terms.push(d.value);
49373                     return terms.some(function(term) {
49374                         return term
49375                             .toString()
49376                             .toLowerCase()
49377                             .indexOf(val.toLowerCase()) !== -1;
49378                     });
49379                 }));
49380             };
49381
49382             var combobox = function(input, attachTo) {
49383                 if (!input || input.empty()) { return; }
49384
49385                 input
49386                     .classed('combobox-input', true)
49387                     .on('focus.combo-input', focus)
49388                     .on('blur.combo-input', blur)
49389                     .on('keydown.combo-input', keydown)
49390                     .on('keyup.combo-input', keyup)
49391                     .on('input.combo-input', change)
49392                     .on('mousedown.combo-input', mousedown)
49393                     .each(function() {
49394                         var parent = this.parentNode;
49395                         var sibling = this.nextSibling;
49396
49397                         select(parent).selectAll('.combobox-caret')
49398                             .filter(function(d) { return d === input.node(); })
49399                             .data([input.node()])
49400                             .enter()
49401                             .insert('div', function() { return sibling; })
49402                             .attr('class', 'combobox-caret')
49403                             .on('mousedown.combo-caret', function() {
49404                                 event.preventDefault(); // don't steal focus from input
49405                                 input.node().focus(); // focus the input as if it was clicked
49406                                 mousedown();
49407                             })
49408                             .on('mouseup.combo-caret', function() {
49409                                 event.preventDefault(); // don't steal focus from input
49410                                 mouseup();
49411                             });
49412                     });
49413
49414
49415                 function mousedown() {
49416                     if (event.button !== 0) { return; }    // left click only
49417                     _tDown = +new Date();
49418
49419                     // clear selection
49420                     var start = input.property('selectionStart');
49421                     var end = input.property('selectionEnd');
49422                     if (start !== end) {
49423                         var val = utilGetSetValue(input);
49424                         input.node().setSelectionRange(val.length, val.length);
49425                         return;
49426                     }
49427
49428                     input.on('mouseup.combo-input', mouseup);
49429                 }
49430
49431
49432                 function mouseup() {
49433                     input.on('mouseup.combo-input', null);
49434                     if (event.button !== 0) { return; }    // left click only
49435                     if (input.node() !== document.activeElement) { return; }   // exit if this input is not focused
49436
49437                     var start = input.property('selectionStart');
49438                     var end = input.property('selectionEnd');
49439                     if (start !== end) { return; }  // exit if user is selecting
49440
49441                     // not showing or showing for a different field - try to show it.
49442                     var combo = container.selectAll('.combobox');
49443                     if (combo.empty() || combo.datum() !== input.node()) {
49444                         var tOrig = _tDown;
49445                         window.setTimeout(function() {
49446                             if (tOrig !== _tDown) { return; }   // exit if user double clicked
49447                             fetchComboData('', function() {
49448                                 show();
49449                                 render();
49450                             });
49451                         }, 250);
49452
49453                     } else {
49454                         hide();
49455                     }
49456                 }
49457
49458
49459                 function focus() {
49460                     fetchComboData('');   // prefetch values (may warm taginfo cache)
49461                 }
49462
49463
49464                 function blur() {
49465                     _comboHideTimerID = window.setTimeout(hide, 75);
49466                 }
49467
49468
49469                 function show() {
49470                     hide();   // remove any existing
49471
49472                     container
49473                         .insert('div', ':first-child')
49474                         .datum(input.node())
49475                         .attr('class', 'combobox' + (klass ? ' combobox-' + klass : ''))
49476                         .style('position', 'absolute')
49477                         .style('display', 'block')
49478                         .style('left', '0px')
49479                         .on('mousedown.combo-container', function () {
49480                             // prevent moving focus out of the input field
49481                             event.preventDefault();
49482                         });
49483
49484                     container
49485                         .on('scroll.combo-scroll', render, true);
49486                 }
49487
49488
49489                 function hide() {
49490                     if (_comboHideTimerID) {
49491                         window.clearTimeout(_comboHideTimerID);
49492                         _comboHideTimerID = undefined;
49493                     }
49494
49495                     container.selectAll('.combobox')
49496                         .remove();
49497
49498                     container
49499                         .on('scroll.combo-scroll', null);
49500                 }
49501
49502
49503                 function keydown() {
49504                     var shown = !container.selectAll('.combobox').empty();
49505                     var tagName = input.node() ? input.node().tagName.toLowerCase() : '';
49506
49507                     switch (event.keyCode) {
49508                         case 8:   // ⌫ Backspace
49509                         case 46:  // ⌦ Delete
49510                             event.stopPropagation();
49511                             _selected = null;
49512                             render();
49513                             input.on('input.combo-input', function() {
49514                                 var start = input.property('selectionStart');
49515                                 input.node().setSelectionRange(start, start);
49516                                 input.on('input.combo-input', change);
49517                             });
49518                             break;
49519
49520                         case 9:   // ⇥ Tab
49521                             accept();
49522                             break;
49523
49524                         case 13:  // ↩ Return
49525                             event.preventDefault();
49526                             event.stopPropagation();
49527                             break;
49528
49529                         case 38:  // ↑ Up arrow
49530                             if (tagName === 'textarea' && !shown) { return; }
49531                             event.preventDefault();
49532                             if (tagName === 'input' && !shown) {
49533                                 show();
49534                             }
49535                             nav(-1);
49536                             break;
49537
49538                         case 40:  // ↓ Down arrow
49539                             if (tagName === 'textarea' && !shown) { return; }
49540                             event.preventDefault();
49541                             if (tagName === 'input' && !shown) {
49542                                 show();
49543                             }
49544                             nav(+1);
49545                             break;
49546                     }
49547                 }
49548
49549
49550                 function keyup() {
49551                     switch (event.keyCode) {
49552                         case 27:  // ⎋ Escape
49553                             cancel();
49554                             break;
49555
49556                         case 13:  // ↩ Return
49557                             accept();
49558                             break;
49559                     }
49560                 }
49561
49562
49563                 // Called whenever the input value is changed (e.g. on typing)
49564                 function change() {
49565                     fetchComboData(value(), function() {
49566                         _selected = null;
49567                         var val = input.property('value');
49568
49569                         if (_suggestions.length) {
49570                             if (input.property('selectionEnd') === val.length) {
49571                                 _selected = tryAutocomplete();
49572                             }
49573
49574                             if (!_selected) {
49575                                 _selected = val;
49576                             }
49577                         }
49578
49579                         if (val.length) {
49580                             var combo = container.selectAll('.combobox');
49581                             if (combo.empty()) {
49582                                 show();
49583                             }
49584                         } else {
49585                             hide();
49586                         }
49587
49588                         render();
49589                     });
49590                 }
49591
49592
49593                 // Called when the user presses up/down arrows to navigate the list
49594                 function nav(dir) {
49595                     if (_suggestions.length) {
49596                         // try to determine previously selected index..
49597                         var index = -1;
49598                         for (var i = 0; i < _suggestions.length; i++) {
49599                             if (_selected && _suggestions[i].value === _selected) {
49600                                 index = i;
49601                                 break;
49602                             }
49603                         }
49604
49605                         // pick new _selected
49606                         index = Math.max(Math.min(index + dir, _suggestions.length - 1), 0);
49607                         _selected = _suggestions[index].value;
49608                         input.property('value', _selected);
49609                     }
49610
49611                     render();
49612                     ensureVisible();
49613                 }
49614
49615
49616                 function ensureVisible() {
49617                     var combo = container.selectAll('.combobox');
49618                     if (combo.empty()) { return; }
49619
49620                     var containerRect = container.node().getBoundingClientRect();
49621                     var comboRect = combo.node().getBoundingClientRect();
49622
49623                     if (comboRect.bottom > containerRect.bottom) {
49624                         var node = attachTo ? attachTo.node() : input.node();
49625                         node.scrollIntoView({ behavior: 'instant', block: 'center' });
49626                         render();
49627                     }
49628
49629                     // https://stackoverflow.com/questions/11039885/scrollintoview-causing-the-whole-page-to-move
49630                     var selected = combo.selectAll('.combobox-option.selected').node();
49631                     if (selected) {
49632                         selected.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
49633                     }
49634                 }
49635
49636
49637                 function value() {
49638                     var value = input.property('value');
49639                     var start = input.property('selectionStart');
49640                     var end = input.property('selectionEnd');
49641
49642                     if (start && end) {
49643                         value = value.substring(0, start);
49644                     }
49645
49646                     return value;
49647                 }
49648
49649
49650                 function fetchComboData(v, cb) {
49651                     _cancelFetch = false;
49652
49653                     _fetcher.call(input, v, function(results) {
49654                         // already chose a value, don't overwrite or autocomplete it
49655                         if (_cancelFetch) { return; }
49656
49657                         _suggestions = results;
49658                         results.forEach(function(d) { _fetched[d.value] = d; });
49659
49660                         if (cb) {
49661                             cb();
49662                         }
49663                     });
49664                 }
49665
49666
49667                 function tryAutocomplete() {
49668                     if (!_canAutocomplete) { return; }
49669
49670                     var val = _caseSensitive ? value() : value().toLowerCase();
49671                     if (!val) { return; }
49672
49673                     // Don't autocomplete if user is typing a number - #4935
49674                     if (!isNaN(parseFloat(val)) && isFinite(val)) { return; }
49675
49676                     var bestIndex = -1;
49677                     for (var i = 0; i < _suggestions.length; i++) {
49678                         var suggestion = _suggestions[i].value;
49679                         var compare = _caseSensitive ? suggestion : suggestion.toLowerCase();
49680
49681                         // if search string matches suggestion exactly, pick it..
49682                         if (compare === val) {
49683                             bestIndex = i;
49684                             break;
49685
49686                         // otherwise lock in the first result that starts with the search string..
49687                         } else if (bestIndex === -1 && compare.indexOf(val) === 0) {
49688                             bestIndex = i;
49689                         }
49690                     }
49691
49692                     if (bestIndex !== -1) {
49693                         var bestVal = _suggestions[bestIndex].value;
49694                         input.property('value', bestVal);
49695                         input.node().setSelectionRange(val.length, bestVal.length);
49696                         return bestVal;
49697                     }
49698                 }
49699
49700
49701                 function render() {
49702                     if (_suggestions.length < _minItems || document.activeElement !== input.node()) {
49703                         hide();
49704                         return;
49705                     }
49706
49707                     var shown = !container.selectAll('.combobox').empty();
49708                     if (!shown) { return; }
49709
49710                     var combo = container.selectAll('.combobox');
49711                     var options = combo.selectAll('.combobox-option')
49712                         .data(_suggestions, function(d) { return d.value; });
49713
49714                     options.exit()
49715                         .remove();
49716
49717                     // enter/update
49718                     options.enter()
49719                         .append('a')
49720                         .attr('class', 'combobox-option')
49721                         .attr('title', function(d) { return d.title; })
49722                         .text(function(d) { return d.display || d.value; })
49723                         .on('mouseenter', _mouseEnterHandler)
49724                         .on('mouseleave', _mouseLeaveHandler)
49725                         .merge(options)
49726                         .classed('selected', function(d) { return d.value === _selected; })
49727                         .on('click.combo-option', accept)
49728                         .order();
49729
49730                     var node = attachTo ? attachTo.node() : input.node();
49731                     var containerRect = container.node().getBoundingClientRect();
49732                     var rect = node.getBoundingClientRect();
49733
49734                     combo
49735                         .style('left', (rect.left + 5 - containerRect.left) + 'px')
49736                         .style('width', (rect.width - 10) + 'px')
49737                         .style('top', (rect.height + rect.top - containerRect.top) + 'px');
49738                 }
49739
49740
49741                 // Dispatches an 'accept' event
49742                 // Then hides the combobox.
49743                 function accept(d) {
49744                     _cancelFetch = true;
49745                     var thiz = input.node();
49746
49747                     if (d) {   // user clicked on a suggestion
49748                         utilGetSetValue(input, d.value);    // replace field contents
49749                         utilTriggerEvent(input, 'change');
49750                     }
49751
49752                     // clear (and keep) selection
49753                     var val = utilGetSetValue(input);
49754                     thiz.setSelectionRange(val.length, val.length);
49755
49756                     d = _fetched[val];
49757                     dispatch$1.call('accept', thiz, d, val);
49758                     hide();
49759                 }
49760
49761
49762                 // Dispatches an 'cancel' event
49763                 // Then hides the combobox.
49764                 function cancel() {
49765                     _cancelFetch = true;
49766                     var thiz = input.node();
49767
49768                     // clear (and remove) selection, and replace field contents
49769                     var val = utilGetSetValue(input);
49770                     var start = input.property('selectionStart');
49771                     var end = input.property('selectionEnd');
49772                     val = val.slice(0, start) + val.slice(end);
49773                     utilGetSetValue(input, val);
49774                     thiz.setSelectionRange(val.length, val.length);
49775
49776                     dispatch$1.call('cancel', thiz);
49777                     hide();
49778                 }
49779
49780             };
49781
49782
49783             combobox.canAutocomplete = function(val) {
49784                 if (!arguments.length) { return _canAutocomplete; }
49785                 _canAutocomplete = val;
49786                 return combobox;
49787             };
49788
49789             combobox.caseSensitive = function(val) {
49790                 if (!arguments.length) { return _caseSensitive; }
49791                 _caseSensitive = val;
49792                 return combobox;
49793             };
49794
49795             combobox.data = function(val) {
49796                 if (!arguments.length) { return _data; }
49797                 _data = val;
49798                 return combobox;
49799             };
49800
49801             combobox.fetcher = function(val) {
49802                 if (!arguments.length) { return _fetcher; }
49803                 _fetcher = val;
49804                 return combobox;
49805             };
49806
49807             combobox.minItems = function(val) {
49808                 if (!arguments.length) { return _minItems; }
49809                 _minItems = val;
49810                 return combobox;
49811             };
49812
49813             combobox.itemsMouseEnter = function(val) {
49814                 if (!arguments.length) { return _mouseEnterHandler; }
49815                 _mouseEnterHandler = val;
49816                 return combobox;
49817             };
49818
49819             combobox.itemsMouseLeave = function(val) {
49820                 if (!arguments.length) { return _mouseLeaveHandler; }
49821                 _mouseLeaveHandler = val;
49822                 return combobox;
49823             };
49824
49825             return utilRebind(combobox, dispatch$1, 'on');
49826         }
49827
49828
49829         uiCombobox.off = function(input, context) {
49830             input
49831                 .on('focus.combo-input', null)
49832                 .on('blur.combo-input', null)
49833                 .on('keydown.combo-input', null)
49834                 .on('keyup.combo-input', null)
49835                 .on('input.combo-input', null)
49836                 .on('mousedown.combo-input', null)
49837                 .on('mouseup.combo-input', null);
49838
49839
49840             context.container()
49841                 .on('scroll.combo-scroll', null);
49842         };
49843
49844         // toggles the visibility of ui elements, using a combination of the
49845         // hide class, which sets display=none, and a d3 transition for opacity.
49846         // this will cause blinking when called repeatedly, so check that the
49847         // value actually changes between calls.
49848         function uiToggle(show, callback) {
49849             return function(selection) {
49850                 selection
49851                     .style('opacity', show ? 0 : 1)
49852                     .classed('hide', false)
49853                     .transition()
49854                     .style('opacity', show ? 1 : 0)
49855                     .on('end', function() {
49856                         select(this)
49857                             .classed('hide', !show)
49858                             .style('opacity', null);
49859                         if (callback) { callback.apply(this); }
49860                     });
49861             };
49862         }
49863
49864         function uiDisclosure(context, key, expandedDefault) {
49865             var dispatch$1 = dispatch('toggled');
49866             var _expanded;
49867             var _title = utilFunctor('');
49868             var _updatePreference = true;
49869             var _content = function () {};
49870
49871
49872             var disclosure = function(selection) {
49873
49874                 if (_expanded === undefined || _expanded === null) {
49875                     // loading _expanded here allows it to be reset by calling `disclosure.expanded(null)`
49876
49877                     var preference = corePreferences('disclosure.' + key + '.expanded');
49878                     _expanded = preference === null ? !!expandedDefault : (preference === 'true');
49879                 }
49880
49881                 var hideToggle = selection.selectAll('.hide-toggle-' + key)
49882                     .data([0]);
49883
49884                 // enter
49885                 var hideToggleEnter = hideToggle.enter()
49886                     .append('a')
49887                     .attr('href', '#')
49888                     .attr('class', 'hide-toggle hide-toggle-' + key)
49889                     .call(svgIcon('', 'pre-text', 'hide-toggle-icon'));
49890
49891                 hideToggleEnter
49892                     .append('span')
49893                     .attr('class', 'hide-toggle-text');
49894
49895                 // update
49896                 hideToggle = hideToggleEnter
49897                     .merge(hideToggle);
49898
49899                 hideToggle
49900                     .on('click', toggle)
49901                     .classed('expanded', _expanded);
49902
49903                 hideToggle.selectAll('.hide-toggle-text')
49904                     .text(_title());
49905
49906                 hideToggle.selectAll('.hide-toggle-icon')
49907                     .attr('xlink:href', _expanded ? '#iD-icon-down'
49908                         : (_mainLocalizer.textDirection() === 'rtl') ? '#iD-icon-backward' : '#iD-icon-forward'
49909                     );
49910
49911
49912                 var wrap = selection.selectAll('.disclosure-wrap')
49913                     .data([0]);
49914
49915                 // enter/update
49916                 wrap = wrap.enter()
49917                     .append('div')
49918                     .attr('class', 'disclosure-wrap disclosure-wrap-' + key)
49919                     .merge(wrap)
49920                     .classed('hide', !_expanded);
49921
49922                 if (_expanded) {
49923                     wrap
49924                         .call(_content);
49925                 }
49926
49927
49928                 function toggle() {
49929                     event.preventDefault();
49930
49931                     _expanded = !_expanded;
49932
49933                     if (_updatePreference) {
49934                         corePreferences('disclosure.' + key + '.expanded', _expanded);
49935                     }
49936
49937                     hideToggle
49938                         .classed('expanded', _expanded);
49939
49940                     hideToggle.selectAll('.hide-toggle-icon')
49941                         .attr('xlink:href', _expanded ? '#iD-icon-down'
49942                             : (_mainLocalizer.textDirection() === 'rtl') ? '#iD-icon-backward' : '#iD-icon-forward'
49943                         );
49944
49945                     wrap
49946                         .call(uiToggle(_expanded));
49947
49948                     if (_expanded) {
49949                         wrap
49950                             .call(_content);
49951                     }
49952
49953                     dispatch$1.call('toggled', this, _expanded);
49954                 }
49955             };
49956
49957
49958             disclosure.title = function(val) {
49959                 if (!arguments.length) { return _title; }
49960                 _title = utilFunctor(val);
49961                 return disclosure;
49962             };
49963
49964
49965             disclosure.expanded = function(val) {
49966                 if (!arguments.length) { return _expanded; }
49967                 _expanded = val;
49968                 return disclosure;
49969             };
49970
49971
49972             disclosure.updatePreference = function(val) {
49973                 if (!arguments.length) { return _updatePreference; }
49974                 _updatePreference = val;
49975                 return disclosure;
49976             };
49977
49978
49979             disclosure.content = function(val) {
49980                 if (!arguments.length) { return _content; }
49981                 _content = val;
49982                 return disclosure;
49983             };
49984
49985
49986             return utilRebind(disclosure, dispatch$1, 'on');
49987         }
49988
49989         // A unit of controls or info to be used in a layout, such as within a pane.
49990         // Can be labeled and collapsible.
49991         function uiSection(id, context) {
49992
49993             var _classes = utilFunctor('');
49994             var _shouldDisplay;
49995             var _content;
49996
49997             var _disclosure;
49998             var _title;
49999             var _expandedByDefault = utilFunctor(true);
50000             var _disclosureContent;
50001             var _disclosureExpanded;
50002
50003             var _containerSelection = select(null);
50004
50005             var section = {
50006                 id: id
50007             };
50008
50009             section.classes = function(val) {
50010                 if (!arguments.length) { return _classes; }
50011                 _classes = utilFunctor(val);
50012                 return section;
50013             };
50014
50015             section.title = function(val) {
50016                 if (!arguments.length) { return _title; }
50017                 _title = utilFunctor(val);
50018                 return section;
50019             };
50020
50021             section.expandedByDefault = function(val) {
50022                 if (!arguments.length) { return _expandedByDefault; }
50023                 _expandedByDefault = utilFunctor(val);
50024                 return section;
50025             };
50026
50027             section.shouldDisplay = function(val) {
50028                 if (!arguments.length) { return _shouldDisplay; }
50029                 _shouldDisplay = utilFunctor(val);
50030                 return section;
50031             };
50032
50033             section.content = function(val) {
50034                 if (!arguments.length) { return _content; }
50035                 _content = val;
50036                 return section;
50037             };
50038
50039             section.disclosureContent = function(val) {
50040                 if (!arguments.length) { return _disclosureContent; }
50041                 _disclosureContent = val;
50042                 return section;
50043             };
50044
50045             section.disclosureExpanded = function(val) {
50046                 if (!arguments.length) { return _disclosureExpanded; }
50047                 _disclosureExpanded = val;
50048                 return section;
50049             };
50050
50051             // may be called multiple times
50052             section.render = function(selection) {
50053
50054                 _containerSelection = selection
50055                     .selectAll('.section-' + id)
50056                     .data([0]);
50057
50058                 var sectionEnter = _containerSelection
50059                     .enter()
50060                     .append('div')
50061                     .attr('class', 'section section-' + id + ' ' + (_classes && _classes() || ''));
50062
50063                 _containerSelection = sectionEnter
50064                     .merge(_containerSelection);
50065
50066                 _containerSelection
50067                     .call(renderContent);
50068             };
50069
50070             section.reRender = function() {
50071                 _containerSelection
50072                     .call(renderContent);
50073             };
50074
50075             section.selection = function() {
50076                 return _containerSelection;
50077             };
50078
50079             section.disclosure = function() {
50080                 return _disclosure;
50081             };
50082
50083             // may be called multiple times
50084             function renderContent(selection) {
50085                 if (_shouldDisplay) {
50086                     var shouldDisplay = _shouldDisplay();
50087                     selection.classed('hide', !shouldDisplay);
50088                     if (!shouldDisplay) {
50089                         selection.html('');
50090                         return;
50091                     }
50092                 }
50093
50094                 if (_disclosureContent) {
50095                     if (!_disclosure) {
50096                         _disclosure = uiDisclosure(context, id.replace(/-/g, '_'), _expandedByDefault())
50097                             .title(_title || '')
50098                             /*.on('toggled', function(expanded) {
50099                                 if (expanded) { selection.node().parentNode.scrollTop += 200; }
50100                             })*/
50101                             .content(_disclosureContent);
50102                     }
50103                     if (_disclosureExpanded !== undefined) {
50104                         _disclosure.expanded(_disclosureExpanded);
50105                         _disclosureExpanded = undefined;
50106                     }
50107                     selection
50108                         .call(_disclosure);
50109
50110                     return;
50111                 }
50112
50113                 if (_content) {
50114                     selection
50115                         .call(_content);
50116                 }
50117             }
50118
50119             return section;
50120         }
50121
50122         // Pass `which` object of the form:
50123         // {
50124         //   key: 'string',     // required
50125         //   value: 'string'    // optional
50126         // }
50127         //   -or-
50128         // {
50129         //   rtype: 'string'    // relation type  (e.g. 'multipolygon')
50130         // }
50131         //   -or-
50132         // {
50133         //   qid: 'string'      // brand wikidata  (e.g. 'Q37158')
50134         // }
50135         //
50136         function uiTagReference(what) {
50137             var wikibase = what.qid ? services.wikidata : services.osmWikibase;
50138             var tagReference = {};
50139
50140             var _button = select(null);
50141             var _body = select(null);
50142             var _loaded;
50143             var _showing;
50144
50145
50146             function load() {
50147                 if (!wikibase) { return; }
50148
50149                 _button
50150                     .classed('tag-reference-loading', true);
50151
50152                 wikibase.getDocs(what, gotDocs);
50153             }
50154
50155
50156             function gotDocs(err, docs) {
50157                 _body.html('');
50158
50159                 if (!docs || !docs.title) {
50160                     _body
50161                         .append('p')
50162                         .attr('class', 'tag-reference-description')
50163                         .text(_t('inspector.no_documentation_key'));
50164                     done();
50165                     return;
50166                 }
50167
50168                 if (docs.imageURL) {
50169                     _body
50170                         .append('img')
50171                         .attr('class', 'tag-reference-wiki-image')
50172                         .attr('src', docs.imageURL)
50173                         .on('load', function() { done(); })
50174                         .on('error', function() { select(this).remove(); done(); });
50175                 } else {
50176                     done();
50177                 }
50178
50179                 _body
50180                     .append('p')
50181                     .attr('class', 'tag-reference-description')
50182                     .text(docs.description || _t('inspector.no_documentation_key'))
50183                     .append('a')
50184                     .attr('class', 'tag-reference-edit')
50185                     .attr('target', '_blank')
50186                     .attr('tabindex', -1)
50187                     .attr('title', _t('inspector.edit_reference'))
50188                     .attr('href', docs.editURL)
50189                     .call(svgIcon('#iD-icon-edit', 'inline'));
50190
50191                 if (docs.wiki) {
50192                     _body
50193                       .append('a')
50194                       .attr('class', 'tag-reference-link')
50195                       .attr('target', '_blank')
50196                       .attr('tabindex', -1)
50197                       .attr('href', docs.wiki.url)
50198                       .call(svgIcon('#iD-icon-out-link', 'inline'))
50199                       .append('span')
50200                       .text(_t(docs.wiki.text));
50201                 }
50202
50203                 // Add link to info about "good changeset comments" - #2923
50204                 if (what.key === 'comment') {
50205                     _body
50206                         .append('a')
50207                         .attr('class', 'tag-reference-comment-link')
50208                         .attr('target', '_blank')
50209                         .attr('tabindex', -1)
50210                         .call(svgIcon('#iD-icon-out-link', 'inline'))
50211                         .attr('href', _t('commit.about_changeset_comments_link'))
50212                         .append('span')
50213                         .text(_t('commit.about_changeset_comments'));
50214                 }
50215             }
50216
50217
50218             function done() {
50219                 _loaded = true;
50220
50221                 _button
50222                     .classed('tag-reference-loading', false);
50223
50224                 _body
50225                     .classed('expanded', true)
50226                     .transition()
50227                     .duration(200)
50228                     .style('max-height', '200px')
50229                     .style('opacity', '1');
50230
50231                 _showing = true;
50232
50233                 _button.selectAll('svg.icon use').each(function() {
50234                     var iconUse = select(this);
50235                     if (iconUse.attr('href') === '#iD-icon-info') {
50236                         iconUse.attr('href', '#iD-icon-info-filled');
50237                     }
50238                 });
50239             }
50240
50241
50242             function hide() {
50243                 _body
50244                     .transition()
50245                     .duration(200)
50246                     .style('max-height', '0px')
50247                     .style('opacity', '0')
50248                     .on('end', function () {
50249                         _body.classed('expanded', false);
50250                     });
50251
50252                 _showing = false;
50253
50254                 _button.selectAll('svg.icon use').each(function() {
50255                     var iconUse = select(this);
50256                     if (iconUse.attr('href') === '#iD-icon-info-filled') {
50257                         iconUse.attr('href', '#iD-icon-info');
50258                     }
50259                 });
50260
50261             }
50262
50263
50264             tagReference.button = function(selection, klass, iconName) {
50265                 _button = selection.selectAll('.tag-reference-button')
50266                     .data([0]);
50267
50268                 _button = _button.enter()
50269                     .append('button')
50270                     .attr('class', 'tag-reference-button ' + klass)
50271                     .attr('title', _t('icons.information'))
50272                     .attr('tabindex', -1)
50273                     .call(svgIcon('#iD-icon-' + (iconName || 'inspect')))
50274                     .merge(_button);
50275
50276                 _button
50277                     .on('click', function () {
50278                         event.stopPropagation();
50279                         event.preventDefault();
50280                         this.blur();    // avoid keeping focus on the button - #4641
50281                         if (_showing) {
50282                             hide();
50283                         } else if (_loaded) {
50284                             done();
50285                         } else {
50286                             load();
50287                         }
50288                     });
50289             };
50290
50291
50292             tagReference.body = function(selection) {
50293                 var itemID = what.qid || what.rtype || (what.key + '-' + what.value);
50294                 _body = selection.selectAll('.tag-reference-body')
50295                     .data([itemID], function(d) { return d; });
50296
50297                 _body.exit()
50298                     .remove();
50299
50300                 _body = _body.enter()
50301                     .append('div')
50302                     .attr('class', 'tag-reference-body')
50303                     .style('max-height', '0')
50304                     .style('opacity', '0')
50305                     .merge(_body);
50306
50307                 if (_showing === false) {
50308                     hide();
50309                 }
50310             };
50311
50312
50313             tagReference.showing = function(val) {
50314                 if (!arguments.length) { return _showing; }
50315                 _showing = val;
50316                 return tagReference;
50317             };
50318
50319
50320             return tagReference;
50321         }
50322
50323         function uiSectionRawTagEditor(id, context) {
50324
50325             var section = uiSection(id, context)
50326                 .classes('raw-tag-editor')
50327                 .title(function() {
50328                     var count = Object.keys(_tags).filter(function(d) { return d; }).length;
50329                     return _t('inspector.title_count', { title: _t('inspector.tags'), count: count });
50330                 })
50331                 .expandedByDefault(false)
50332                 .disclosureContent(renderDisclosureContent);
50333
50334             var taginfo = services.taginfo;
50335             var dispatch$1 = dispatch('change');
50336             var availableViews = [
50337                 { id: 'text', icon: '#fas-i-cursor' },
50338                 { id: 'list', icon: '#fas-th-list' }
50339             ];
50340
50341             var _tagView = (corePreferences('raw-tag-editor-view') || 'list');   // 'list, 'text'
50342             var _readOnlyTags = [];
50343             // the keys in the order we want them to display
50344             var _orderedKeys = [];
50345             var _showBlank = false;
50346             var _pendingChange = null;
50347             var _state;
50348             var _presets;
50349             var _tags;
50350             var _entityIDs;
50351
50352             function renderDisclosureContent(wrap) {
50353
50354                 // remove deleted keys
50355                 _orderedKeys = _orderedKeys.filter(function(key) {
50356                     return _tags[key] !== undefined;
50357                 });
50358
50359                 // When switching to a different entity or changing the state (hover/select)
50360                 // reorder the keys alphabetically.
50361                 // We trigger this by emptying the `_orderedKeys` array, then it will be rebuilt here.
50362                 // Otherwise leave their order alone - #5857, #5927
50363                 var all = Object.keys(_tags).sort();
50364                 var missingKeys = utilArrayDifference(all, _orderedKeys);
50365                 for (var i in missingKeys) {
50366                     _orderedKeys.push(missingKeys[i]);
50367                 }
50368
50369                 // assemble row data
50370                 var rowData = _orderedKeys.map(function(key, i) {
50371                     return { index: i, key: key, value: _tags[key] };
50372                 });
50373
50374                 // append blank row last, if necessary
50375                 if (!rowData.length || _showBlank) {
50376                     _showBlank = false;
50377                     rowData.push({ index: rowData.length, key: '', value: '' });
50378                 }
50379
50380
50381                 // View Options
50382                 var options = wrap.selectAll('.raw-tag-options')
50383                     .data([0]);
50384
50385                 options.exit()
50386                     .remove();
50387
50388                 var optionsEnter = options.enter()
50389                     .insert('div', ':first-child')
50390                     .attr('class', 'raw-tag-options');
50391
50392                 var optionEnter = optionsEnter.selectAll('.raw-tag-option')
50393                     .data(availableViews, function(d) { return d.id; })
50394                     .enter();
50395
50396                 optionEnter
50397                     .append('button')
50398                     .attr('class', function(d) {
50399                         return 'raw-tag-option raw-tag-option-' + d.id + (_tagView === d.id ? ' selected' : '');
50400                     })
50401                     .attr('title', function(d) { return _t('icons.' + d.id); })
50402                     .on('click', function(d) {
50403                         _tagView = d.id;
50404                         corePreferences('raw-tag-editor-view', d.id);
50405
50406                         wrap.selectAll('.raw-tag-option')
50407                             .classed('selected', function(datum) { return datum === d; });
50408
50409                         wrap.selectAll('.tag-text')
50410                             .classed('hide', (d.id !== 'text'))
50411                             .each(setTextareaHeight);
50412
50413                         wrap.selectAll('.tag-list, .add-row')
50414                             .classed('hide', (d.id !== 'list'));
50415                     })
50416                     .each(function(d) {
50417                         select(this)
50418                             .call(svgIcon(d.icon));
50419                     });
50420
50421
50422                 // View as Text
50423                 var textData = rowsToText(rowData);
50424                 var textarea = wrap.selectAll('.tag-text')
50425                     .data([0]);
50426
50427                 textarea = textarea.enter()
50428                     .append('textarea')
50429                     .attr('class', 'tag-text' + (_tagView !== 'text' ? ' hide' : ''))
50430                     .call(utilNoAuto)
50431                     .attr('placeholder', _t('inspector.key_value'))
50432                     .attr('spellcheck', 'false')
50433                     .merge(textarea);
50434
50435                 textarea
50436                     .call(utilGetSetValue, textData)
50437                     .each(setTextareaHeight)
50438                     .on('input', setTextareaHeight)
50439                     .on('blur', textChanged)
50440                     .on('change', textChanged);
50441
50442
50443                 // View as List
50444                 var list = wrap.selectAll('.tag-list')
50445                     .data([0]);
50446
50447                 list = list.enter()
50448                     .append('ul')
50449                     .attr('class', 'tag-list' + (_tagView !== 'list' ? ' hide' : ''))
50450                     .merge(list);
50451
50452
50453                 // Container for the Add button
50454                 var addRowEnter = wrap.selectAll('.add-row')
50455                     .data([0])
50456                     .enter()
50457                     .append('div')
50458                     .attr('class', 'add-row' + (_tagView !== 'list' ? ' hide' : ''));
50459
50460                 addRowEnter
50461                     .append('button')
50462                     .attr('class', 'add-tag')
50463                     .call(svgIcon('#iD-icon-plus', 'light'))
50464                     .on('click', addTag);
50465
50466                 addRowEnter
50467                     .append('div')
50468                     .attr('class', 'space-value');   // preserve space
50469
50470                 addRowEnter
50471                     .append('div')
50472                     .attr('class', 'space-buttons');  // preserve space
50473
50474
50475                 // Tag list items
50476                 var items = list.selectAll('.tag-row')
50477                     .data(rowData, function(d) { return d.key; });
50478
50479                 items.exit()
50480                     .each(unbind)
50481                     .remove();
50482
50483
50484                 // Enter
50485                 var itemsEnter = items.enter()
50486                     .append('li')
50487                     .attr('class', 'tag-row')
50488                     .classed('readonly', isReadOnly);
50489
50490                 var innerWrap = itemsEnter.append('div')
50491                     .attr('class', 'inner-wrap');
50492
50493                 innerWrap
50494                     .append('div')
50495                     .attr('class', 'key-wrap')
50496                     .append('input')
50497                     .property('type', 'text')
50498                     .attr('class', 'key')
50499                     .call(utilNoAuto)
50500                     .on('blur', keyChange)
50501                     .on('change', keyChange);
50502
50503                 innerWrap
50504                     .append('div')
50505                     .attr('class', 'value-wrap')
50506                     .append('input')
50507                     .property('type', 'text')
50508                     .attr('class', 'value')
50509                     .call(utilNoAuto)
50510                     .on('blur', valueChange)
50511                     .on('change', valueChange)
50512                     .on('keydown.push-more', pushMore);
50513
50514                 innerWrap
50515                     .append('button')
50516                     .attr('tabindex', -1)
50517                     .attr('class', 'form-field-button remove')
50518                     .attr('title', _t('icons.remove'))
50519                     .call(svgIcon('#iD-operation-delete'));
50520
50521
50522                 // Update
50523                 items = items
50524                     .merge(itemsEnter)
50525                     .sort(function(a, b) { return a.index - b.index; });
50526
50527                 items
50528                     .each(function(d) {
50529                         var row = select(this);
50530                         var key = row.select('input.key');      // propagate bound data
50531                         var value = row.select('input.value');  // propagate bound data
50532
50533                         if (_entityIDs && taginfo && _state !== 'hover') {
50534                             bindTypeahead(key, value);
50535                         }
50536
50537                         var reference;
50538
50539                         if (typeof d.value !== 'string') {
50540                             reference = uiTagReference({ key: d.key });
50541                         } else {
50542                             var isRelation = _entityIDs && _entityIDs.some(function(entityID) {
50543                                 return context.entity(entityID).type === 'relation';
50544                             });
50545                             if (isRelation && d.key === 'type') {
50546                                 reference = uiTagReference({ rtype: d.value });
50547                             } else {
50548                                 reference = uiTagReference({ key: d.key, value: d.value });
50549                             }
50550                         }
50551
50552                         if (_state === 'hover') {
50553                             reference.showing(false);
50554                         }
50555
50556                         row.select('.inner-wrap')      // propagate bound data
50557                             .call(reference.button);
50558
50559                         row.call(reference.body);
50560
50561                         row.select('button.remove');   // propagate bound data
50562                     });
50563
50564                 items.selectAll('input.key')
50565                     .attr('title', function(d) { return d.key; })
50566                     .call(utilGetSetValue, function(d) { return d.key; })
50567                     .attr('readonly', function(d) {
50568                         return (isReadOnly(d) || (typeof d.value !== 'string')) || null;
50569                     });
50570
50571                 items.selectAll('input.value')
50572                     .attr('title', function(d) {
50573                         return Array.isArray(d.value) ? d.value.filter(Boolean).join('\n') : d.value;
50574                     })
50575                     .classed('mixed', function(d) {
50576                         return Array.isArray(d.value);
50577                     })
50578                     .attr('placeholder', function(d) {
50579                         return typeof d.value === 'string' ? null : _t('inspector.multiple_values');
50580                     })
50581                     .call(utilGetSetValue, function(d) {
50582                         return typeof d.value === 'string' ? d.value : '';
50583                     })
50584                     .attr('readonly', function(d) {
50585                         return isReadOnly(d) || null;
50586                     });
50587
50588                 items.selectAll('button.remove')
50589                     .on(('PointerEvent' in window ? 'pointer' : 'mouse') + 'down', removeTag);  // 'click' fires too late - #5878
50590
50591             }
50592
50593             function isReadOnly(d) {
50594                 for (var i = 0; i < _readOnlyTags.length; i++) {
50595                     if (d.key.match(_readOnlyTags[i]) !== null) {
50596                         return true;
50597                     }
50598                 }
50599                 return false;
50600             }
50601
50602             function setTextareaHeight() {
50603                 if (_tagView !== 'text') { return; }
50604
50605                 var selection = select(this);
50606                 selection.style('height', null);
50607                 selection.style('height', selection.node().scrollHeight + 5 + 'px');
50608             }
50609
50610             function stringify(s) {
50611                 return JSON.stringify(s).slice(1, -1);   // without leading/trailing "
50612             }
50613
50614             function unstringify(s) {
50615                 var leading = '';
50616                 var trailing = '';
50617                 if (s.length < 1 || s.charAt(0) !== '"') {
50618                     leading = '"';
50619                 }
50620                 if (s.length < 2 || s.charAt(s.length - 1) !== '"' ||
50621                     (s.charAt(s.length - 1) === '"' && s.charAt(s.length - 2) === '\\')
50622                 ) {
50623                     trailing = '"';
50624                 }
50625                 return JSON.parse(leading + s + trailing);
50626             }
50627
50628             function rowsToText(rows) {
50629                 var str = rows
50630                     .filter(function(row) { return row.key && row.key.trim() !== ''; })
50631                     .map(function(row) {
50632                         var rawVal = row.value;
50633                         if (typeof rawVal !== 'string') { rawVal = '*'; }
50634                         var val = rawVal ? stringify(rawVal) : '';
50635                         return stringify(row.key) + '=' + val;
50636                     })
50637                     .join('\n');
50638
50639                 if (_state !== 'hover' && str.length) {
50640                     return str + '\n';
50641                 }
50642                 return  str;
50643             }
50644
50645             function textChanged() {
50646                 var newText = this.value.trim();
50647                 var newTags = {};
50648                 newText.split('\n').forEach(function(row) {
50649                     var m = row.match(/^\s*([^=]+)=(.*)$/);
50650                     if (m !== null) {
50651                         var k = context.cleanTagKey(unstringify(m[1].trim()));
50652                         var v = context.cleanTagValue(unstringify(m[2].trim()));
50653                         newTags[k] = v;
50654                     }
50655                 });
50656
50657                 var tagDiff = utilTagDiff(_tags, newTags);
50658                 if (!tagDiff.length) { return; }
50659
50660                 _pendingChange  = _pendingChange || {};
50661
50662                 tagDiff.forEach(function(change) {
50663                     if (isReadOnly({ key: change.key })) { return; }
50664
50665                     // skip unchanged multiselection placeholders
50666                     if (change.newVal === '*' && typeof change.oldVal !== 'string') { return; }
50667
50668                     if (change.type === '-') {
50669                         _pendingChange[change.key] = undefined;
50670                     } else if (change.type === '+') {
50671                         _pendingChange[change.key] = change.newVal || '';
50672                     }
50673                 });
50674
50675                 if (Object.keys(_pendingChange).length === 0) {
50676                     _pendingChange = null;
50677                     return;
50678                 }
50679
50680                 scheduleChange();
50681             }
50682
50683             function pushMore() {
50684                 // if pressing Tab on the last value field with content, add a blank row
50685                 if (event.keyCode === 9 && !event.shiftKey &&
50686                     section.selection().selectAll('.tag-list li:last-child input.value').node() === this &&
50687                     utilGetSetValue(select(this))) {
50688                     addTag();
50689                 }
50690             }
50691
50692             function bindTypeahead(key, value) {
50693                 if (isReadOnly(key.datum())) { return; }
50694
50695                 if (Array.isArray(value.datum().value)) {
50696                     value.call(uiCombobox(context, 'tag-value')
50697                         .minItems(1)
50698                         .fetcher(function(value, callback) {
50699                             var keyString = utilGetSetValue(key);
50700                             if (!_tags[keyString]) { return; }
50701                             var data = _tags[keyString].filter(Boolean).map(function(tagValue) {
50702                                 return {
50703                                     value: tagValue,
50704                                     title: tagValue
50705                                 };
50706                             });
50707                             callback(data);
50708                         }));
50709                     return;
50710                 }
50711
50712                 var geometry = context.graph().geometry(_entityIDs[0]);
50713
50714                 key.call(uiCombobox(context, 'tag-key')
50715                     .fetcher(function(value, callback) {
50716                         taginfo.keys({
50717                             debounce: true,
50718                             geometry: geometry,
50719                             query: value
50720                         }, function(err, data) {
50721                             if (!err) {
50722                                 var filtered = data.filter(function(d) { return _tags[d.value] === undefined; });
50723                                 callback(sort(value, filtered));
50724                             }
50725                         });
50726                     }));
50727
50728                 value.call(uiCombobox(context, 'tag-value')
50729                     .fetcher(function(value, callback) {
50730                         taginfo.values({
50731                             debounce: true,
50732                             key: utilGetSetValue(key),
50733                             geometry: geometry,
50734                             query: value
50735                         }, function(err, data) {
50736                             if (!err) { callback(sort(value, data)); }
50737                         });
50738                     }));
50739
50740
50741                 function sort(value, data) {
50742                     var sameletter = [];
50743                     var other = [];
50744                     for (var i = 0; i < data.length; i++) {
50745                         if (data[i].value.substring(0, value.length) === value) {
50746                             sameletter.push(data[i]);
50747                         } else {
50748                             other.push(data[i]);
50749                         }
50750                     }
50751                     return sameletter.concat(other);
50752                 }
50753             }
50754
50755             function unbind() {
50756                 var row = select(this);
50757
50758                 row.selectAll('input.key')
50759                     .call(uiCombobox.off, context);
50760
50761                 row.selectAll('input.value')
50762                     .call(uiCombobox.off, context);
50763             }
50764
50765             function keyChange(d) {
50766                 if (select(this).attr('readonly')) { return; }
50767
50768                 var kOld = d.key;
50769
50770                 // exit if we are currently about to delete this row anyway - #6366
50771                 if (_pendingChange && _pendingChange.hasOwnProperty(kOld) && _pendingChange[kOld] === undefined) { return; }
50772
50773                 var kNew = context.cleanTagKey(this.value.trim());
50774
50775                 // allow no change if the key should be readonly
50776                 if (isReadOnly({ key: kNew })) {
50777                     this.value = kOld;
50778                     return;
50779                 }
50780
50781                 if (kNew &&
50782                     kNew !== kOld &&
50783                     _tags[kNew] !== undefined) {
50784                     // new key is already in use, switch focus to the existing row
50785
50786                     this.value = kOld;                // reset the key
50787                     section.selection().selectAll('.tag-list input.value')
50788                         .each(function(d) {
50789                             if (d.key === kNew) {     // send focus to that other value combo instead
50790                                 var input = select(this).node();
50791                                 input.focus();
50792                                 input.select();
50793                             }
50794                         });
50795                     return;
50796                 }
50797
50798
50799                 var row = this.parentNode.parentNode;
50800                 var inputVal = select(row).selectAll('input.value');
50801                 var vNew = context.cleanTagValue(utilGetSetValue(inputVal));
50802
50803                 _pendingChange = _pendingChange || {};
50804
50805                 if (kOld) {
50806                     _pendingChange[kOld] = undefined;
50807                 }
50808
50809                 _pendingChange[kNew] = vNew;
50810
50811                 // update the ordered key index so this row doesn't change position
50812                 var existingKeyIndex = _orderedKeys.indexOf(kOld);
50813                 if (existingKeyIndex !== -1) { _orderedKeys[existingKeyIndex] = kNew; }
50814
50815                 d.key = kNew;    // update datum to avoid exit/enter on tag update
50816                 d.value = vNew;
50817
50818                 this.value = kNew;
50819                 utilGetSetValue(inputVal, vNew);
50820                 scheduleChange();
50821             }
50822
50823             function valueChange(d) {
50824                 if (isReadOnly(d)) { return; }
50825
50826                 // exit if this is a multiselection and no value was entered
50827                 if (typeof d.value !== 'string' && !this.value) { return; }
50828
50829                 // exit if we are currently about to delete this row anyway - #6366
50830                 if (_pendingChange && _pendingChange.hasOwnProperty(d.key) && _pendingChange[d.key] === undefined) { return; }
50831
50832                 _pendingChange = _pendingChange || {};
50833
50834                 _pendingChange[d.key] = context.cleanTagValue(this.value);
50835                 scheduleChange();
50836             }
50837
50838             function removeTag(d) {
50839                 if (isReadOnly(d)) { return; }
50840
50841                 if (d.key === '') {    // removing the blank row
50842                     _showBlank = false;
50843                     section.reRender();
50844
50845                 } else {
50846                     // remove the key from the ordered key index
50847                     _orderedKeys = _orderedKeys.filter(function(key) { return key !== d.key; });
50848
50849                     _pendingChange  = _pendingChange || {};
50850                     _pendingChange[d.key] = undefined;
50851                     scheduleChange();
50852                 }
50853             }
50854
50855             function addTag() {
50856                 // Delay render in case this click is blurring an edited combo.
50857                 // Without the setTimeout, the `content` render would wipe out the pending tag change.
50858                 window.setTimeout(function() {
50859                     _showBlank = true;
50860                     section.reRender();
50861                     section.selection().selectAll('.tag-list li:last-child input.key').node().focus();
50862                 }, 20);
50863             }
50864
50865             function scheduleChange() {
50866                 // Cache IDs in case the editor is reloaded before the change event is called. - #6028
50867                 var entityIDs = _entityIDs;
50868
50869                 // Delay change in case this change is blurring an edited combo. - #5878
50870                 window.setTimeout(function() {
50871                     if (!_pendingChange) { return; }
50872
50873                     dispatch$1.call('change', this, entityIDs, _pendingChange);
50874                     _pendingChange = null;
50875                 }, 10);
50876             }
50877
50878
50879             section.state = function(val) {
50880                 if (!arguments.length) { return _state; }
50881                 if (_state !== val) {
50882                     _orderedKeys = [];
50883                     _state = val;
50884                 }
50885                 return section;
50886             };
50887
50888
50889             section.presets = function(val) {
50890                 if (!arguments.length) { return _presets; }
50891                 _presets = val;
50892                 if (_presets && _presets.length && _presets[0].isFallback()) {
50893                     section.disclosureExpanded(true);
50894                 } else {
50895                     section.disclosureExpanded(null);
50896                 }
50897                 return section;
50898             };
50899
50900
50901             section.tags = function(val) {
50902                 if (!arguments.length) { return _tags; }
50903                 _tags = val;
50904                 return section;
50905             };
50906
50907
50908             section.entityIDs = function(val) {
50909                 if (!arguments.length) { return _entityIDs; }
50910                 if (!_entityIDs || !val || !utilArrayIdentical(_entityIDs, val)) {
50911                     _entityIDs = val;
50912                     _orderedKeys = [];
50913                 }
50914                 return section;
50915             };
50916
50917
50918             // pass an array of regular expressions to test against the tag key
50919             section.readOnlyTags = function(val) {
50920                 if (!arguments.length) { return _readOnlyTags; }
50921                 _readOnlyTags = val;
50922                 return section;
50923             };
50924
50925
50926             return utilRebind(section, dispatch$1, 'on');
50927         }
50928
50929         function uiDataEditor(context) {
50930             var dataHeader = uiDataHeader();
50931             var rawTagEditor = uiSectionRawTagEditor('custom-data-tag-editor', context)
50932                 .expandedByDefault(true)
50933                 .readOnlyTags([/./]);
50934             var _datum;
50935
50936
50937             function dataEditor(selection) {
50938
50939                 var header = selection.selectAll('.header')
50940                     .data([0]);
50941
50942                 var headerEnter = header.enter()
50943                     .append('div')
50944                     .attr('class', 'header fillL');
50945
50946                 headerEnter
50947                     .append('button')
50948                     .attr('class', 'close')
50949                     .on('click', function() {
50950                         context.enter(modeBrowse(context));
50951                     })
50952                     .call(svgIcon('#iD-icon-close'));
50953
50954                 headerEnter
50955                     .append('h3')
50956                     .text(_t('map_data.title'));
50957
50958
50959                 var body = selection.selectAll('.body')
50960                     .data([0]);
50961
50962                 body = body.enter()
50963                     .append('div')
50964                     .attr('class', 'body')
50965                     .merge(body);
50966
50967                 var editor = body.selectAll('.data-editor')
50968                     .data([0]);
50969
50970                 // enter/update
50971                 editor.enter()
50972                     .append('div')
50973                     .attr('class', 'modal-section data-editor')
50974                     .merge(editor)
50975                     .call(dataHeader.datum(_datum));
50976
50977                 var rte = body.selectAll('.raw-tag-editor')
50978                     .data([0]);
50979
50980                 // enter/update
50981                 rte.enter()
50982                     .append('div')
50983                     .attr('class', 'raw-tag-editor data-editor')
50984                     .merge(rte)
50985                     .call(rawTagEditor
50986                         .tags((_datum && _datum.properties) || {})
50987                         .state('hover')
50988                         .render
50989                     )
50990                     .selectAll('textarea.tag-text')
50991                     .attr('readonly', true)
50992                     .classed('readonly', true);
50993             }
50994
50995
50996             dataEditor.datum = function(val) {
50997                 if (!arguments.length) { return _datum; }
50998                 _datum = val;
50999                 return this;
51000             };
51001
51002
51003             return dataEditor;
51004         }
51005
51006         function modeSelectData(context, selectedDatum) {
51007             var mode = {
51008                 id: 'select-data',
51009                 button: 'browse'
51010             };
51011
51012             var keybinding = utilKeybinding('select-data');
51013             var dataEditor = uiDataEditor(context);
51014
51015             var behaviors = [
51016                 behaviorBreathe(),
51017                 behaviorHover(context),
51018                 behaviorSelect(context),
51019                 behaviorLasso(context),
51020                 modeDragNode(context).behavior,
51021                 modeDragNote(context).behavior
51022             ];
51023
51024
51025             // class the data as selected, or return to browse mode if the data is gone
51026             function selectData(drawn) {
51027                 var selection = context.surface().selectAll('.layer-mapdata .data' + selectedDatum.__featurehash__);
51028
51029                 if (selection.empty()) {
51030                     // Return to browse mode if selected DOM elements have
51031                     // disappeared because the user moved them out of view..
51032                     var source = event && event.type === 'zoom' && event.sourceEvent;
51033                     if (drawn && source && (source.type === 'pointermove' || source.type === 'mousemove' || source.type === 'touchmove')) {
51034                         context.enter(modeBrowse(context));
51035                     }
51036                 } else {
51037                     selection.classed('selected', true);
51038                 }
51039             }
51040
51041
51042             function esc() {
51043                 if (context.container().select('.combobox').size()) { return; }
51044                 context.enter(modeBrowse(context));
51045             }
51046
51047
51048             mode.zoomToSelected = function() {
51049                 var extent = geoExtent(d3_geoBounds(selectedDatum));
51050                 context.map().centerZoomEase(extent.center(), context.map().trimmedExtentZoom(extent));
51051             };
51052
51053
51054             mode.enter = function() {
51055                 behaviors.forEach(context.install);
51056
51057                 keybinding
51058                     .on(_t('inspector.zoom_to.key'), mode.zoomToSelected)
51059                     .on('⎋', esc, true);
51060
51061                 select(document)
51062                     .call(keybinding);
51063
51064                 selectData();
51065
51066                 var sidebar = context.ui().sidebar;
51067                 sidebar.show(dataEditor.datum(selectedDatum));
51068
51069                 // expand the sidebar, avoid obscuring the data if needed
51070                 var extent = geoExtent(d3_geoBounds(selectedDatum));
51071                 sidebar.expand(sidebar.intersects(extent));
51072
51073                 context.map()
51074                     .on('drawn.select-data', selectData);
51075             };
51076
51077
51078             mode.exit = function() {
51079                 behaviors.forEach(context.uninstall);
51080
51081                 select(document)
51082                     .call(keybinding.unbind);
51083
51084                 context.surface()
51085                     .selectAll('.layer-mapdata .selected')
51086                     .classed('selected hover', false);
51087
51088                 context.map()
51089                     .on('drawn.select-data', null);
51090
51091                 context.ui().sidebar
51092                     .hide();
51093             };
51094
51095
51096             return mode;
51097         }
51098
51099         function uiImproveOsmComments() {
51100           var _qaItem;
51101
51102           function issueComments(selection) {
51103             // make the div immediately so it appears above the buttons
51104             var comments = selection.selectAll('.comments-container')
51105               .data([0]);
51106
51107             comments = comments.enter()
51108               .append('div')
51109                 .attr('class', 'comments-container')
51110               .merge(comments);
51111
51112             // must retrieve comments from API before they can be displayed
51113             services.improveOSM.getComments(_qaItem)
51114               .then(function (d) {
51115                 if (!d.comments) { return; } // nothing to do here
51116
51117                 var commentEnter = comments.selectAll('.comment')
51118                   .data(d.comments)
51119                   .enter()
51120                   .append('div')
51121                     .attr('class', 'comment');
51122
51123                 commentEnter
51124                   .append('div')
51125                     .attr('class', 'comment-avatar')
51126                     .call(svgIcon('#iD-icon-avatar', 'comment-avatar-icon'));
51127
51128                 var mainEnter = commentEnter
51129                   .append('div')
51130                   .attr('class', 'comment-main');
51131
51132                 var metadataEnter = mainEnter
51133                   .append('div')
51134                     .attr('class', 'comment-metadata');
51135
51136                 metadataEnter
51137                   .append('div')
51138                     .attr('class', 'comment-author')
51139                     .each(function(d) {
51140                       var osm = services.osm;
51141                       var selection = select(this);
51142                       if (osm && d.username) {
51143                         selection = selection
51144                           .append('a')
51145                           .attr('class', 'comment-author-link')
51146                           .attr('href', osm.userURL(d.username))
51147                           .attr('tabindex', -1)
51148                           .attr('target', '_blank');
51149                       }
51150                       selection
51151                         .text(function (d) { return d.username; });
51152                     });
51153
51154                 metadataEnter
51155                   .append('div')
51156                     .attr('class', 'comment-date')
51157                     .text(function (d) { return _t('note.status.commented', { when: localeDateString(d.timestamp) }); });
51158
51159                 mainEnter
51160                   .append('div')
51161                     .attr('class', 'comment-text')
51162                   .append('p')
51163                     .text(function (d) { return d.text; });
51164             })
51165             .catch(function (err) {
51166               console.log(err); // eslint-disable-line no-console
51167             });
51168           }
51169
51170           function localeDateString(s) {
51171             if (!s) { return null; }
51172             var options = { day: 'numeric', month: 'short', year: 'numeric' };
51173             var d = new Date(s * 1000); // timestamp is served in seconds, date takes ms
51174             if (isNaN(d.getTime())) { return null; }
51175             return d.toLocaleDateString(_mainLocalizer.localeCode(), options);
51176           }
51177
51178           issueComments.issue = function(val) {
51179             if (!arguments.length) { return _qaItem; }
51180             _qaItem = val;
51181             return issueComments;
51182           };
51183
51184           return issueComments;
51185         }
51186
51187         function uiImproveOsmDetails(context) {
51188           var _qaItem;
51189
51190
51191           function issueDetail(d) {
51192             if (d.desc) { return d.desc; }
51193             var issueKey = d.issueKey;
51194             d.replacements = d.replacements || {};
51195             d.replacements.default = _t('inspector.unknown');  // special key `default` works as a fallback string
51196             return _t(("QA.improveOSM.error_types." + issueKey + ".description"), d.replacements);
51197           }
51198
51199
51200           function improveOsmDetails(selection) {
51201             var details = selection.selectAll('.error-details')
51202               .data(
51203                 (_qaItem ? [_qaItem] : []),
51204                 function (d) { return ((d.id) + "-" + (d.status || 0)); }
51205               );
51206
51207             details.exit()
51208               .remove();
51209
51210             var detailsEnter = details.enter()
51211               .append('div')
51212                 .attr('class', 'error-details qa-details-container');
51213
51214
51215             // description
51216             var descriptionEnter = detailsEnter
51217               .append('div')
51218                 .attr('class', 'qa-details-subsection');
51219
51220             descriptionEnter
51221               .append('h4')
51222                 .text(function () { return _t('QA.keepRight.detail_description'); });
51223
51224             descriptionEnter
51225               .append('div')
51226                 .attr('class', 'qa-details-description-text')
51227                 .html(issueDetail);
51228
51229             // If there are entity links in the error message..
51230             var relatedEntities = [];
51231             descriptionEnter.selectAll('.error_entity_link, .error_object_link')
51232               .each(function() {
51233                 var link = select(this);
51234                 var isObjectLink = link.classed('error_object_link');
51235                 var entityID = isObjectLink ?
51236                   (utilEntityRoot(_qaItem.objectType) + _qaItem.objectId)
51237                   : this.textContent;
51238                 var entity = context.hasEntity(entityID);
51239
51240                 relatedEntities.push(entityID);
51241
51242                 // Add click handler
51243                 link
51244                   .on('mouseenter', function () {
51245                     utilHighlightEntities([entityID], true, context);
51246                   })
51247                   .on('mouseleave', function () {
51248                     utilHighlightEntities([entityID], false, context);
51249                   })
51250                   .on('click', function () {
51251                     event.preventDefault();
51252
51253                     utilHighlightEntities([entityID], false, context);
51254
51255                     var osmlayer = context.layers().layer('osm');
51256                     if (!osmlayer.enabled()) {
51257                       osmlayer.enabled(true);
51258                     }
51259
51260                     context.map().centerZoom(_qaItem.loc, 20);
51261
51262                     if (entity) {
51263                       context.enter(modeSelect(context, [entityID]));
51264                     } else {
51265                       context.loadEntity(entityID, function () {
51266                         context.enter(modeSelect(context, [entityID]));
51267                       });
51268                     }
51269                   });
51270
51271                 // Replace with friendly name if possible
51272                 // (The entity may not yet be loaded into the graph)
51273                 if (entity) {
51274                   var name = utilDisplayName(entity);  // try to use common name
51275
51276                   if (!name && !isObjectLink) {
51277                     var preset = _mainPresetIndex.match(entity, context.graph());
51278                     name = preset && !preset.isFallback() && preset.name();  // fallback to preset name
51279                   }
51280
51281                   if (name) {
51282                     this.innerText = name;
51283                   }
51284                 }
51285               });
51286
51287             // Don't hide entities related to this error - #5880
51288             context.features().forceVisible(relatedEntities);
51289             context.map().pan([0,0]);  // trigger a redraw
51290           }
51291
51292           improveOsmDetails.issue = function(val) {
51293             if (!arguments.length) { return _qaItem; }
51294             _qaItem = val;
51295             return improveOsmDetails;
51296           };
51297
51298           return improveOsmDetails;
51299         }
51300
51301         function uiImproveOsmHeader() {
51302           var _qaItem;
51303
51304
51305           function issueTitle(d) {
51306             var issueKey = d.issueKey;
51307             d.replacements = d.replacements || {};
51308             d.replacements.default = _t('inspector.unknown');  // special key `default` works as a fallback string
51309             return _t(("QA.improveOSM.error_types." + issueKey + ".title"), d.replacements);
51310           }
51311
51312
51313           function improveOsmHeader(selection) {
51314             var header = selection.selectAll('.qa-header')
51315               .data(
51316                 (_qaItem ? [_qaItem] : []),
51317                 function (d) { return ((d.id) + "-" + (d.status || 0)); }
51318               );
51319
51320             header.exit()
51321               .remove();
51322
51323             var headerEnter = header.enter()
51324               .append('div')
51325                 .attr('class', 'qa-header');
51326
51327             var svgEnter = headerEnter
51328               .append('div')
51329                 .attr('class', 'qa-header-icon')
51330                 .classed('new', function (d) { return d.id < 0; })
51331               .append('svg')
51332                 .attr('width', '20px')
51333                 .attr('height', '30px')
51334                 .attr('viewbox', '0 0 20 30')
51335                 .attr('class', function (d) { return ("preset-icon-28 qaItem " + (d.service) + " itemId-" + (d.id) + " itemType-" + (d.itemType)); });
51336
51337             svgEnter
51338               .append('polygon')
51339                 .attr('fill', 'currentColor')
51340                 .attr('class', 'qaItem-fill')
51341                 .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');
51342
51343             svgEnter
51344               .append('use')
51345                 .attr('class', 'icon-annotation')
51346                 .attr('width', '13px')
51347                 .attr('height', '13px')
51348                 .attr('transform', 'translate(3.5, 5)')
51349                 .attr('xlink:href', function (d) {
51350                   var picon = d.icon;
51351                   if (!picon) {
51352                     return '';
51353                   } else {
51354                     var isMaki = /^maki-/.test(picon);
51355                     return ("#" + picon + (isMaki ? '-11' : ''));
51356                   }
51357                 });
51358
51359             headerEnter
51360               .append('div')
51361                 .attr('class', 'qa-header-label')
51362                 .text(issueTitle);
51363           }
51364
51365           improveOsmHeader.issue = function(val) {
51366             if (!arguments.length) { return _qaItem; }
51367             _qaItem = val;
51368             return improveOsmHeader;
51369           };
51370
51371           return improveOsmHeader;
51372         }
51373
51374         function uiImproveOsmEditor(context) {
51375           var dispatch$1 = dispatch('change');
51376           var qaDetails = uiImproveOsmDetails(context);
51377           var qaComments = uiImproveOsmComments();
51378           var qaHeader = uiImproveOsmHeader();
51379
51380           var _qaItem;
51381
51382           function improveOsmEditor(selection) {
51383
51384             var headerEnter = selection.selectAll('.header')
51385               .data([0])
51386               .enter()
51387               .append('div')
51388                 .attr('class', 'header fillL');
51389
51390             headerEnter
51391               .append('button')
51392                 .attr('class', 'close')
51393                 .on('click', function () { return context.enter(modeBrowse(context)); })
51394                 .call(svgIcon('#iD-icon-close'));
51395
51396             headerEnter
51397               .append('h3')
51398                 .text(_t('QA.improveOSM.title'));
51399
51400             var body = selection.selectAll('.body')
51401               .data([0]);
51402
51403             body = body.enter()
51404               .append('div')
51405                 .attr('class', 'body')
51406               .merge(body);
51407
51408             var editor = body.selectAll('.qa-editor')
51409               .data([0]);
51410
51411             editor.enter()
51412               .append('div')
51413                 .attr('class', 'modal-section qa-editor')
51414               .merge(editor)
51415                 .call(qaHeader.issue(_qaItem))
51416                 .call(qaDetails.issue(_qaItem))
51417                 .call(qaComments.issue(_qaItem))
51418                 .call(improveOsmSaveSection);
51419           }
51420
51421           function improveOsmSaveSection(selection) {
51422             var isSelected = (_qaItem && _qaItem.id === context.selectedErrorID());
51423             var isShown = (_qaItem && (isSelected || _qaItem.newComment || _qaItem.comment));
51424             var saveSection = selection.selectAll('.qa-save')
51425               .data(
51426                 (isShown ? [_qaItem] : []),
51427                 function (d) { return ((d.id) + "-" + (d.status || 0)); }
51428               );
51429
51430             // exit
51431             saveSection.exit()
51432               .remove();
51433
51434             // enter
51435             var saveSectionEnter = saveSection.enter()
51436               .append('div')
51437                 .attr('class', 'qa-save save-section cf');
51438
51439             saveSectionEnter
51440               .append('h4')
51441                 .attr('class', '.qa-save-header')
51442                 .text(_t('note.newComment'));
51443
51444             saveSectionEnter
51445               .append('textarea')
51446                 .attr('class', 'new-comment-input')
51447                 .attr('placeholder', _t('QA.keepRight.comment_placeholder'))
51448                 .attr('maxlength', 1000)
51449                 .property('value', function (d) { return d.newComment; })
51450                 .call(utilNoAuto)
51451                 .on('input', changeInput)
51452                 .on('blur', changeInput);
51453
51454             // update
51455             saveSection = saveSectionEnter
51456               .merge(saveSection)
51457                 .call(qaSaveButtons);
51458
51459             function changeInput() {
51460               var input = select(this);
51461               var val = input.property('value').trim();
51462
51463               if (val === '') {
51464                 val = undefined;
51465               }
51466
51467               // store the unsaved comment with the issue itself
51468               _qaItem = _qaItem.update({ newComment: val });
51469
51470               var qaService = services.improveOSM;
51471               if (qaService) {
51472                 qaService.replaceItem(_qaItem);
51473               }
51474
51475               saveSection
51476                 .call(qaSaveButtons);
51477             }
51478           }
51479
51480           function qaSaveButtons(selection) {
51481             var isSelected = (_qaItem && _qaItem.id === context.selectedErrorID());
51482             var buttonSection = selection.selectAll('.buttons')
51483               .data((isSelected ? [_qaItem] : []), function (d) { return d.status + d.id; });
51484
51485             // exit
51486             buttonSection.exit()
51487               .remove();
51488
51489             // enter
51490             var buttonEnter = buttonSection.enter()
51491               .append('div')
51492                 .attr('class', 'buttons');
51493
51494             buttonEnter
51495               .append('button')
51496                 .attr('class', 'button comment-button action')
51497                 .text(_t('QA.keepRight.save_comment'));
51498
51499             buttonEnter
51500               .append('button')
51501                 .attr('class', 'button close-button action');
51502
51503             buttonEnter
51504               .append('button')
51505                 .attr('class', 'button ignore-button action');
51506
51507             // update
51508             buttonSection = buttonSection
51509               .merge(buttonEnter);
51510
51511             buttonSection.select('.comment-button')
51512               .attr('disabled', function (d) { return d.newComment ? null : true; })
51513               .on('click.comment', function(d) {
51514                 this.blur();    // avoid keeping focus on the button - #4641
51515                 var qaService = services.improveOSM;
51516                 if (qaService) {
51517                   qaService.postUpdate(d, function (err, item) { return dispatch$1.call('change', item); });
51518                 }
51519               });
51520
51521             buttonSection.select('.close-button')
51522               .text(function (d) {
51523                 var andComment = (d.newComment ? '_comment' : '');
51524                 return _t(("QA.keepRight.close" + andComment));
51525               })
51526               .on('click.close', function(d) {
51527                 this.blur();    // avoid keeping focus on the button - #4641
51528                 var qaService = services.improveOSM;
51529                 if (qaService) {
51530                   d.newStatus = 'SOLVED';
51531                   qaService.postUpdate(d, function (err, item) { return dispatch$1.call('change', item); });
51532                 }
51533               });
51534
51535             buttonSection.select('.ignore-button')
51536               .text(function (d) {
51537                 var andComment = (d.newComment ? '_comment' : '');
51538                 return _t(("QA.keepRight.ignore" + andComment));
51539               })
51540               .on('click.ignore', function(d) {
51541                 this.blur();    // avoid keeping focus on the button - #4641
51542                 var qaService = services.improveOSM;
51543                 if (qaService) {
51544                   d.newStatus = 'INVALID';
51545                   qaService.postUpdate(d, function (err, item) { return dispatch$1.call('change', item); });
51546                 }
51547               });
51548           }
51549
51550           // NOTE: Don't change method name until UI v3 is merged
51551           improveOsmEditor.error = function(val) {
51552             if (!arguments.length) { return _qaItem; }
51553             _qaItem = val;
51554             return improveOsmEditor;
51555           };
51556
51557           return utilRebind(improveOsmEditor, dispatch$1, 'on');
51558         }
51559
51560         function uiKeepRightDetails(context) {
51561           var _qaItem;
51562
51563
51564           function issueDetail(d) {
51565             var itemType = d.itemType;
51566             var parentIssueType = d.parentIssueType;
51567             var unknown = _t('inspector.unknown');
51568             var replacements = d.replacements || {};
51569             replacements.default = unknown;  // special key `default` works as a fallback string
51570
51571             var detail = _t(("QA.keepRight.errorTypes." + itemType + ".description"), replacements);
51572             if (detail === unknown) {
51573               detail = _t(("QA.keepRight.errorTypes." + parentIssueType + ".description"), replacements);
51574             }
51575             return detail;
51576           }
51577
51578
51579           function keepRightDetails(selection) {
51580             var details = selection.selectAll('.error-details')
51581               .data(
51582                 (_qaItem ? [_qaItem] : []),
51583                 function (d) { return ((d.id) + "-" + (d.status || 0)); }
51584               );
51585
51586             details.exit()
51587               .remove();
51588
51589             var detailsEnter = details.enter()
51590               .append('div')
51591                 .attr('class', 'error-details qa-details-container');
51592
51593             // description
51594             var descriptionEnter = detailsEnter
51595               .append('div')
51596                 .attr('class', 'qa-details-subsection');
51597
51598             descriptionEnter
51599               .append('h4')
51600                 .text(function () { return _t('QA.keepRight.detail_description'); });
51601
51602             descriptionEnter
51603               .append('div')
51604                 .attr('class', 'qa-details-description-text')
51605                 .html(issueDetail);
51606
51607             // If there are entity links in the error message..
51608             var relatedEntities = [];
51609             descriptionEnter.selectAll('.error_entity_link, .error_object_link')
51610               .each(function() {
51611                 var link = select(this);
51612                 var isObjectLink = link.classed('error_object_link');
51613                 var entityID = isObjectLink ?
51614                   (utilEntityRoot(_qaItem.objectType) + _qaItem.objectId)
51615                   : this.textContent;
51616                 var entity = context.hasEntity(entityID);
51617
51618                 relatedEntities.push(entityID);
51619
51620                 // Add click handler
51621                 link
51622                   .on('mouseenter', function () {
51623                     utilHighlightEntities([entityID], true, context);
51624                   })
51625                   .on('mouseleave', function () {
51626                     utilHighlightEntities([entityID], false, context);
51627                   })
51628                   .on('click', function () {
51629                     event.preventDefault();
51630
51631                     utilHighlightEntities([entityID], false, context);
51632
51633                     var osmlayer = context.layers().layer('osm');
51634                     if (!osmlayer.enabled()) {
51635                       osmlayer.enabled(true);
51636                     }
51637
51638                     context.map().centerZoomEase(_qaItem.loc, 20);
51639
51640                     if (entity) {
51641                       context.enter(modeSelect(context, [entityID]));
51642                     } else {
51643                       context.loadEntity(entityID, function () {
51644                         context.enter(modeSelect(context, [entityID]));
51645                       });
51646                     }
51647                   });
51648
51649                 // Replace with friendly name if possible
51650                 // (The entity may not yet be loaded into the graph)
51651                 if (entity) {
51652                   var name = utilDisplayName(entity);  // try to use common name
51653
51654                   if (!name && !isObjectLink) {
51655                     var preset = _mainPresetIndex.match(entity, context.graph());
51656                     name = preset && !preset.isFallback() && preset.name();  // fallback to preset name
51657                   }
51658
51659                   if (name) {
51660                     this.innerText = name;
51661                   }
51662                 }
51663               });
51664
51665             // Don't hide entities related to this issue - #5880
51666             context.features().forceVisible(relatedEntities);
51667             context.map().pan([0,0]);  // trigger a redraw
51668           }
51669
51670           keepRightDetails.issue = function(val) {
51671             if (!arguments.length) { return _qaItem; }
51672             _qaItem = val;
51673             return keepRightDetails;
51674           };
51675
51676           return keepRightDetails;
51677         }
51678
51679         function uiKeepRightHeader() {
51680           var _qaItem;
51681
51682
51683           function issueTitle(d) {
51684             var itemType = d.itemType;
51685             var parentIssueType = d.parentIssueType;
51686             var unknown = _t('inspector.unknown');
51687             var replacements = d.replacements || {};
51688             replacements.default = unknown;  // special key `default` works as a fallback string
51689
51690             var title = _t(("QA.keepRight.errorTypes." + itemType + ".title"), replacements);
51691             if (title === unknown) {
51692               title = _t(("QA.keepRight.errorTypes." + parentIssueType + ".title"), replacements);
51693             }
51694             return title;
51695           }
51696
51697
51698           function keepRightHeader(selection) {
51699             var header = selection.selectAll('.qa-header')
51700               .data(
51701                 (_qaItem ? [_qaItem] : []),
51702                 function (d) { return ((d.id) + "-" + (d.status || 0)); }
51703               );
51704
51705             header.exit()
51706               .remove();
51707
51708             var headerEnter = header.enter()
51709               .append('div')
51710                 .attr('class', 'qa-header');
51711
51712             var iconEnter = headerEnter
51713               .append('div')
51714                 .attr('class', 'qa-header-icon')
51715                 .classed('new', function (d) { return d.id < 0; });
51716
51717             iconEnter
51718               .append('div')
51719                 .attr('class', function (d) { return ("preset-icon-28 qaItem " + (d.service) + " itemId-" + (d.id) + " itemType-" + (d.parentIssueType)); })
51720                 .call(svgIcon('#iD-icon-bolt', 'qaItem-fill'));
51721
51722             headerEnter
51723               .append('div')
51724                 .attr('class', 'qa-header-label')
51725                 .text(issueTitle);
51726           }
51727
51728
51729           keepRightHeader.issue = function(val) {
51730             if (!arguments.length) { return _qaItem; }
51731             _qaItem = val;
51732             return keepRightHeader;
51733           };
51734
51735           return keepRightHeader;
51736         }
51737
51738         function uiViewOnKeepRight() {
51739           var _qaItem;
51740
51741           function viewOnKeepRight(selection) {
51742             var url;
51743             if (services.keepRight && (_qaItem instanceof QAItem)) {
51744               url = services.keepRight.issueURL(_qaItem);
51745             }
51746
51747             var link = selection.selectAll('.view-on-keepRight')
51748               .data(url ? [url] : []);
51749
51750             // exit
51751             link.exit()
51752               .remove();
51753
51754             // enter
51755             var linkEnter = link.enter()
51756               .append('a')
51757                 .attr('class', 'view-on-keepRight')
51758                 .attr('target', '_blank')
51759                 .attr('rel', 'noopener') // security measure
51760                 .attr('href', function (d) { return d; })
51761                 .call(svgIcon('#iD-icon-out-link', 'inline'));
51762
51763             linkEnter
51764               .append('span')
51765                 .text(_t('inspector.view_on_keepRight'));
51766           }
51767
51768           viewOnKeepRight.what = function(val) {
51769             if (!arguments.length) { return _qaItem; }
51770             _qaItem = val;
51771             return viewOnKeepRight;
51772           };
51773
51774           return viewOnKeepRight;
51775         }
51776
51777         function uiKeepRightEditor(context) {
51778           var dispatch$1 = dispatch('change');
51779           var qaDetails = uiKeepRightDetails(context);
51780           var qaHeader = uiKeepRightHeader();
51781
51782           var _qaItem;
51783
51784           function keepRightEditor(selection) {
51785
51786             var headerEnter = selection.selectAll('.header')
51787               .data([0])
51788               .enter()
51789               .append('div')
51790                 .attr('class', 'header fillL');
51791
51792             headerEnter
51793               .append('button')
51794                 .attr('class', 'close')
51795                 .on('click', function () { return context.enter(modeBrowse(context)); })
51796                 .call(svgIcon('#iD-icon-close'));
51797
51798             headerEnter
51799               .append('h3')
51800                 .text(_t('QA.keepRight.title'));
51801
51802
51803             var body = selection.selectAll('.body')
51804               .data([0]);
51805
51806             body = body.enter()
51807               .append('div')
51808                 .attr('class', 'body')
51809               .merge(body);
51810
51811             var editor = body.selectAll('.qa-editor')
51812               .data([0]);
51813
51814             editor.enter()
51815               .append('div')
51816                 .attr('class', 'modal-section qa-editor')
51817               .merge(editor)
51818                 .call(qaHeader.issue(_qaItem))
51819                 .call(qaDetails.issue(_qaItem))
51820                 .call(keepRightSaveSection);
51821
51822
51823             var footer = selection.selectAll('.footer')
51824               .data([0]);
51825
51826             footer.enter()
51827               .append('div')
51828               .attr('class', 'footer')
51829               .merge(footer)
51830               .call(uiViewOnKeepRight().what(_qaItem));
51831           }
51832
51833
51834           function keepRightSaveSection(selection) {
51835             var isSelected = (_qaItem && _qaItem.id === context.selectedErrorID());
51836             var isShown = (_qaItem && (isSelected || _qaItem.newComment || _qaItem.comment));
51837             var saveSection = selection.selectAll('.qa-save')
51838               .data(
51839                 (isShown ? [_qaItem] : []),
51840                 function (d) { return ((d.id) + "-" + (d.status || 0)); }
51841               );
51842
51843             // exit
51844             saveSection.exit()
51845               .remove();
51846
51847             // enter
51848             var saveSectionEnter = saveSection.enter()
51849               .append('div')
51850                 .attr('class', 'qa-save save-section cf');
51851
51852             saveSectionEnter
51853               .append('h4')
51854                 .attr('class', '.qa-save-header')
51855                 .text(_t('QA.keepRight.comment'));
51856
51857             saveSectionEnter
51858               .append('textarea')
51859                 .attr('class', 'new-comment-input')
51860                 .attr('placeholder', _t('QA.keepRight.comment_placeholder'))
51861                 .attr('maxlength', 1000)
51862                 .property('value', function (d) { return d.newComment || d.comment; })
51863                 .call(utilNoAuto)
51864                 .on('input', changeInput)
51865                 .on('blur', changeInput);
51866
51867             // update
51868             saveSection = saveSectionEnter
51869               .merge(saveSection)
51870                 .call(qaSaveButtons);
51871
51872             function changeInput() {
51873               var input = select(this);
51874               var val = input.property('value').trim();
51875
51876               if (val === _qaItem.comment) {
51877                 val = undefined;
51878               }
51879
51880               // store the unsaved comment with the issue itself
51881               _qaItem = _qaItem.update({ newComment: val });
51882
51883               var qaService = services.keepRight;
51884               if (qaService) {
51885                 qaService.replaceItem(_qaItem);  // update keepright cache
51886               }
51887
51888               saveSection
51889                 .call(qaSaveButtons);
51890             }
51891           }
51892
51893
51894           function qaSaveButtons(selection) {
51895             var isSelected = (_qaItem && _qaItem.id === context.selectedErrorID());
51896             var buttonSection = selection.selectAll('.buttons')
51897               .data((isSelected ? [_qaItem] : []), function (d) { return d.status + d.id; });
51898
51899             // exit
51900             buttonSection.exit()
51901               .remove();
51902
51903             // enter
51904             var buttonEnter = buttonSection.enter()
51905               .append('div')
51906                 .attr('class', 'buttons');
51907
51908             buttonEnter
51909               .append('button')
51910                 .attr('class', 'button comment-button action')
51911                 .text(_t('QA.keepRight.save_comment'));
51912
51913             buttonEnter
51914               .append('button')
51915                 .attr('class', 'button close-button action');
51916
51917             buttonEnter
51918               .append('button')
51919                 .attr('class', 'button ignore-button action');
51920
51921             // update
51922             buttonSection = buttonSection
51923               .merge(buttonEnter);
51924
51925             buttonSection.select('.comment-button')   // select and propagate data
51926               .attr('disabled', function (d) { return d.newComment ? null : true; })
51927               .on('click.comment', function(d) {
51928                 this.blur();    // avoid keeping focus on the button - #4641
51929                 var qaService = services.keepRight;
51930                 if (qaService) {
51931                   qaService.postUpdate(d, function (err, item) { return dispatch$1.call('change', item); });
51932                 }
51933               });
51934
51935             buttonSection.select('.close-button')   // select and propagate data
51936               .text(function (d) {
51937                 var andComment = (d.newComment ? '_comment' : '');
51938                 return _t(("QA.keepRight.close" + andComment));
51939               })
51940               .on('click.close', function(d) {
51941                 this.blur();    // avoid keeping focus on the button - #4641
51942                 var qaService = services.keepRight;
51943                 if (qaService) {
51944                   d.newStatus = 'ignore_t';   // ignore temporarily (item fixed)
51945                   qaService.postUpdate(d, function (err, item) { return dispatch$1.call('change', item); });
51946                 }
51947               });
51948
51949             buttonSection.select('.ignore-button')   // select and propagate data
51950               .text(function (d) {
51951                 var andComment = (d.newComment ? '_comment' : '');
51952                 return _t(("QA.keepRight.ignore" + andComment));
51953               })
51954               .on('click.ignore', function(d) {
51955                 this.blur();    // avoid keeping focus on the button - #4641
51956                 var qaService = services.keepRight;
51957                 if (qaService) {
51958                   d.newStatus = 'ignore';   // ignore permanently (false positive)
51959                   qaService.postUpdate(d, function (err, item) { return dispatch$1.call('change', item); });
51960                 }
51961               });
51962           }
51963
51964           // NOTE: Don't change method name until UI v3 is merged
51965           keepRightEditor.error = function(val) {
51966             if (!arguments.length) { return _qaItem; }
51967             _qaItem = val;
51968             return keepRightEditor;
51969           };
51970
51971           return utilRebind(keepRightEditor, dispatch$1, 'on');
51972         }
51973
51974         function uiOsmoseDetails(context) {
51975           var _qaItem;
51976
51977           function issueString(d, type) {
51978             if (!d) { return ''; }
51979
51980             // Issue strings are cached from Osmose API
51981             var s = services.osmose.getStrings(d.itemType);
51982             return (type in s) ? s[type] : '';
51983           }
51984
51985
51986           function osmoseDetails(selection) {
51987             var details = selection.selectAll('.error-details')
51988               .data(
51989                 _qaItem ? [_qaItem] : [],
51990                 function (d) { return ((d.id) + "-" + (d.status || 0)); }
51991               );
51992
51993             details.exit()
51994               .remove();
51995
51996             var detailsEnter = details.enter()
51997               .append('div')
51998                 .attr('class', 'error-details qa-details-container');
51999
52000
52001             // Description
52002             if (issueString(_qaItem, 'detail')) {
52003               var div = detailsEnter
52004                 .append('div')
52005                   .attr('class', 'qa-details-subsection');
52006
52007               div
52008                 .append('h4')
52009                   .text(function () { return _t('QA.keepRight.detail_description'); });
52010
52011               div
52012                 .append('p')
52013                   .attr('class', 'qa-details-description-text')
52014                   .html(function (d) { return issueString(d, 'detail'); })
52015                 .selectAll('a')
52016                   .attr('rel', 'noopener')
52017                   .attr('target', '_blank');
52018             }
52019
52020             // Elements (populated later as data is requested)
52021             var detailsDiv = detailsEnter
52022               .append('div')
52023                 .attr('class', 'qa-details-subsection');
52024
52025             var elemsDiv = detailsEnter
52026               .append('div')
52027                 .attr('class', 'qa-details-subsection');
52028
52029             // Suggested Fix (musn't exist for every issue type)
52030             if (issueString(_qaItem, 'fix')) {
52031               var div$1 = detailsEnter
52032                 .append('div')
52033                   .attr('class', 'qa-details-subsection');
52034
52035               div$1
52036                 .append('h4')
52037                   .text(function () { return _t('QA.osmose.fix_title'); });
52038
52039               div$1
52040                 .append('p')
52041                   .html(function (d) { return issueString(d, 'fix'); })
52042                 .selectAll('a')
52043                   .attr('rel', 'noopener')
52044                   .attr('target', '_blank');
52045             }
52046
52047             // Common Pitfalls (musn't exist for every issue type)
52048             if (issueString(_qaItem, 'trap')) {
52049               var div$2 = detailsEnter
52050                 .append('div')
52051                   .attr('class', 'qa-details-subsection');
52052
52053               div$2
52054                 .append('h4')
52055                   .text(function () { return _t('QA.osmose.trap_title'); });
52056
52057               div$2
52058                 .append('p')
52059                   .html(function (d) { return issueString(d, 'trap'); })
52060                 .selectAll('a')
52061                   .attr('rel', 'noopener')
52062                   .attr('target', '_blank');
52063             }
52064
52065             // Save current item to check if UI changed by time request resolves
52066             var thisItem = _qaItem;
52067             services.osmose.loadIssueDetail(_qaItem)
52068               .then(function (d) {
52069                 // No details to add if there are no associated issue elements
52070                 if (!d.elems || d.elems.length === 0) { return; }
52071
52072                 // Do nothing if UI has moved on by the time this resolves
52073                 if (
52074                   context.selectedErrorID() !== thisItem.id
52075                   && context.container().selectAll((".qaItem.osmose.hover.itemId-" + (thisItem.id))).empty()
52076                 ) { return; }
52077
52078                 // Things like keys and values are dynamically added to a subtitle string
52079                 if (d.detail) {
52080                   detailsDiv
52081                     .append('h4')
52082                       .text(function () { return _t('QA.osmose.detail_title'); });
52083
52084                   detailsDiv
52085                     .append('p')
52086                       .html(function (d) { return d.detail; })
52087                     .selectAll('a')
52088                       .attr('rel', 'noopener')
52089                       .attr('target', '_blank');
52090                 }
52091
52092                 // Create list of linked issue elements
52093                 elemsDiv
52094                   .append('h4')
52095                     .text(function () { return _t('QA.osmose.elems_title'); });
52096
52097                 elemsDiv
52098                   .append('ul').selectAll('li')
52099                   .data(d.elems)
52100                   .enter()
52101                   .append('li')
52102                   .append('a')
52103                     .attr('class', 'error_entity_link')
52104                     .text(function (d) { return d; })
52105                     .each(function() {
52106                       var link = select(this);
52107                       var entityID = this.textContent;
52108                       var entity = context.hasEntity(entityID);
52109
52110                       // Add click handler
52111                       link
52112                         .on('mouseenter', function () {
52113                           utilHighlightEntities([entityID], true, context);
52114                         })
52115                         .on('mouseleave', function () {
52116                           utilHighlightEntities([entityID], false, context);
52117                         })
52118                         .on('click', function () {
52119                           event.preventDefault();
52120
52121                           utilHighlightEntities([entityID], false, context);
52122
52123                           var osmlayer = context.layers().layer('osm');
52124                           if (!osmlayer.enabled()) {
52125                             osmlayer.enabled(true);
52126                           }
52127
52128                           context.map().centerZoom(d.loc, 20);
52129
52130                           if (entity) {
52131                             context.enter(modeSelect(context, [entityID]));
52132                           } else {
52133                             context.loadEntity(entityID, function () {
52134                               context.enter(modeSelect(context, [entityID]));
52135                             });
52136                           }
52137                         });
52138
52139                       // Replace with friendly name if possible
52140                       // (The entity may not yet be loaded into the graph)
52141                       if (entity) {
52142                         var name = utilDisplayName(entity);  // try to use common name
52143
52144                         if (!name) {
52145                           var preset = _mainPresetIndex.match(entity, context.graph());
52146                           name = preset && !preset.isFallback() && preset.name();  // fallback to preset name
52147                         }
52148
52149                         if (name) {
52150                           this.innerText = name;
52151                         }
52152                       }
52153                     });
52154
52155                 // Don't hide entities related to this issue - #5880
52156                 context.features().forceVisible(d.elems);
52157                 context.map().pan([0,0]);  // trigger a redraw
52158               })
52159               .catch(function (err) {
52160                 console.log(err); // eslint-disable-line no-console
52161               });
52162           }
52163
52164
52165           osmoseDetails.issue = function(val) {
52166             if (!arguments.length) { return _qaItem; }
52167             _qaItem = val;
52168             return osmoseDetails;
52169           };
52170
52171
52172           return osmoseDetails;
52173         }
52174
52175         function uiOsmoseHeader() {
52176           var _qaItem;
52177
52178           function issueTitle(d) {
52179             var unknown = _t('inspector.unknown');
52180
52181             if (!d) { return unknown; }
52182
52183             // Issue titles supplied by Osmose
52184             var s = services.osmose.getStrings(d.itemType);
52185             return ('title' in s) ? s.title : unknown;
52186           }
52187
52188           function osmoseHeader(selection) {
52189             var header = selection.selectAll('.qa-header')
52190               .data(
52191                 (_qaItem ? [_qaItem] : []),
52192                 function (d) { return ((d.id) + "-" + (d.status || 0)); }
52193               );
52194
52195             header.exit()
52196               .remove();
52197
52198             var headerEnter = header.enter()
52199               .append('div')
52200                 .attr('class', 'qa-header');
52201
52202             var svgEnter = headerEnter
52203               .append('div')
52204                 .attr('class', 'qa-header-icon')
52205                 .classed('new', function (d) { return d.id < 0; })
52206               .append('svg')
52207                 .attr('width', '20px')
52208                 .attr('height', '30px')
52209                 .attr('viewbox', '0 0 20 30')
52210                 .attr('class', function (d) { return ("preset-icon-28 qaItem " + (d.service) + " itemId-" + (d.id) + " itemType-" + (d.itemType)); });
52211
52212             svgEnter
52213               .append('polygon')
52214                 .attr('fill', function (d) { return services.osmose.getColor(d.item); })
52215                 .attr('class', 'qaItem-fill')
52216                 .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');
52217
52218             svgEnter
52219               .append('use')
52220                 .attr('class', 'icon-annotation')
52221                 .attr('width', '13px')
52222                 .attr('height', '13px')
52223                 .attr('transform', 'translate(3.5, 5)')
52224                 .attr('xlink:href', function (d) {
52225                   var picon = d.icon;
52226
52227                   if (!picon) {
52228                     return '';
52229                   } else {
52230                     var isMaki = /^maki-/.test(picon);
52231                     return ("#" + picon + (isMaki ? '-11' : ''));
52232                   }
52233                 });
52234
52235             headerEnter
52236               .append('div')
52237                 .attr('class', 'qa-header-label')
52238                 .text(issueTitle);
52239           }
52240
52241           osmoseHeader.issue = function(val) {
52242             if (!arguments.length) { return _qaItem; }
52243             _qaItem = val;
52244             return osmoseHeader;
52245           };
52246
52247           return osmoseHeader;
52248         }
52249
52250         function uiViewOnOsmose() {
52251           var _qaItem;
52252
52253           function viewOnOsmose(selection) {
52254             var url;
52255             if (services.osmose && (_qaItem instanceof QAItem)) {
52256               url = services.osmose.itemURL(_qaItem);
52257             }
52258
52259             var link = selection.selectAll('.view-on-osmose')
52260               .data(url ? [url] : []);
52261
52262             // exit
52263             link.exit()
52264               .remove();
52265
52266             // enter
52267             var linkEnter = link.enter()
52268               .append('a')
52269                 .attr('class', 'view-on-osmose')
52270                 .attr('target', '_blank')
52271                 .attr('rel', 'noopener') // security measure
52272                 .attr('href', function (d) { return d; })
52273                 .call(svgIcon('#iD-icon-out-link', 'inline'));
52274
52275             linkEnter
52276               .append('span')
52277                 .text(_t('inspector.view_on_osmose'));
52278           }
52279
52280           viewOnOsmose.what = function(val) {
52281             if (!arguments.length) { return _qaItem; }
52282             _qaItem = val;
52283             return viewOnOsmose;
52284           };
52285
52286           return viewOnOsmose;
52287         }
52288
52289         function uiOsmoseEditor(context) {
52290           var dispatch$1 = dispatch('change');
52291           var qaDetails = uiOsmoseDetails(context);
52292           var qaHeader = uiOsmoseHeader();
52293
52294           var _qaItem;
52295
52296           function osmoseEditor(selection) {
52297
52298             var header = selection.selectAll('.header')
52299               .data([0]);
52300
52301             var headerEnter = header.enter()
52302               .append('div')
52303                 .attr('class', 'header fillL');
52304
52305             headerEnter
52306               .append('button')
52307                 .attr('class', 'close')
52308                 .on('click', function () { return context.enter(modeBrowse(context)); })
52309                 .call(svgIcon('#iD-icon-close'));
52310
52311             headerEnter
52312               .append('h3')
52313                 .text(_t('QA.osmose.title'));
52314
52315             var body = selection.selectAll('.body')
52316               .data([0]);
52317
52318             body = body.enter()
52319                 .append('div')
52320                 .attr('class', 'body')
52321               .merge(body);
52322
52323             var editor = body.selectAll('.qa-editor')
52324               .data([0]);
52325
52326             editor.enter()
52327               .append('div')
52328                 .attr('class', 'modal-section qa-editor')
52329               .merge(editor)
52330                 .call(qaHeader.issue(_qaItem))
52331                 .call(qaDetails.issue(_qaItem))
52332                 .call(osmoseSaveSection);
52333
52334             var footer = selection.selectAll('.footer')
52335               .data([0]);
52336
52337             footer.enter()
52338               .append('div')
52339               .attr('class', 'footer')
52340               .merge(footer)
52341               .call(uiViewOnOsmose().what(_qaItem));
52342           }
52343
52344           function osmoseSaveSection(selection) {
52345             var isSelected = (_qaItem && _qaItem.id === context.selectedErrorID());
52346             var isShown = (_qaItem && isSelected);
52347             var saveSection = selection.selectAll('.qa-save')
52348               .data(
52349                 (isShown ? [_qaItem] : []),
52350                 function (d) { return ((d.id) + "-" + (d.status || 0)); }
52351               );
52352
52353             // exit
52354             saveSection.exit()
52355               .remove();
52356
52357             // enter
52358             var saveSectionEnter = saveSection.enter()
52359               .append('div')
52360                 .attr('class', 'qa-save save-section cf');
52361
52362             // update
52363             saveSection = saveSectionEnter
52364               .merge(saveSection)
52365                 .call(qaSaveButtons);
52366           }
52367
52368           function qaSaveButtons(selection) {
52369             var isSelected = (_qaItem && _qaItem.id === context.selectedErrorID());
52370             var buttonSection = selection.selectAll('.buttons')
52371               .data((isSelected ? [_qaItem] : []), function (d) { return d.status + d.id; });
52372
52373             // exit
52374             buttonSection.exit()
52375               .remove();
52376
52377             // enter
52378             var buttonEnter = buttonSection.enter()
52379               .append('div')
52380                 .attr('class', 'buttons');
52381
52382             buttonEnter
52383               .append('button')
52384                 .attr('class', 'button close-button action');
52385
52386             buttonEnter
52387               .append('button')
52388                 .attr('class', 'button ignore-button action');
52389
52390             // update
52391             buttonSection = buttonSection
52392               .merge(buttonEnter);
52393
52394             buttonSection.select('.close-button')
52395               .text(function () { return _t('QA.keepRight.close'); })
52396               .on('click.close', function(d) {
52397                 this.blur();    // avoid keeping focus on the button - #4641
52398                 var qaService = services.osmose;
52399                 if (qaService) {
52400                   d.newStatus = 'done';
52401                   qaService.postUpdate(d, function (err, item) { return dispatch$1.call('change', item); });
52402                 }
52403               });
52404
52405             buttonSection.select('.ignore-button')
52406               .text(function () { return _t('QA.keepRight.ignore'); })
52407               .on('click.ignore', function(d) {
52408                 this.blur();    // avoid keeping focus on the button - #4641
52409                 var qaService = services.osmose;
52410                 if (qaService) {
52411                   d.newStatus = 'false';
52412                   qaService.postUpdate(d, function (err, item) { return dispatch$1.call('change', item); });
52413                 }
52414               });
52415           }
52416
52417           // NOTE: Don't change method name until UI v3 is merged
52418           osmoseEditor.error = function(val) {
52419             if (!arguments.length) { return _qaItem; }
52420             _qaItem = val;
52421             return osmoseEditor;
52422           };
52423
52424           return utilRebind(osmoseEditor, dispatch$1, 'on');
52425         }
52426
52427         // NOTE: Don't change name of this until UI v3 is merged
52428         function modeSelectError(context, selectedErrorID, selectedErrorService) {
52429             var mode = {
52430                 id: 'select-error',
52431                 button: 'browse'
52432             };
52433
52434             var keybinding = utilKeybinding('select-error');
52435
52436             var errorService = services[selectedErrorService];
52437             var errorEditor;
52438             switch (selectedErrorService) {
52439                 case 'improveOSM':
52440                     errorEditor = uiImproveOsmEditor(context)
52441                     .on('change', function() {
52442                         context.map().pan([0,0]);  // trigger a redraw
52443                         var error = checkSelectedID();
52444                         if (!error) { return; }
52445                         context.ui().sidebar
52446                             .show(errorEditor.error(error));
52447                     });
52448                     break;
52449                 case 'keepRight':
52450                     errorEditor = uiKeepRightEditor(context)
52451                     .on('change', function() {
52452                         context.map().pan([0,0]);  // trigger a redraw
52453                         var error = checkSelectedID();
52454                         if (!error) { return; }
52455                         context.ui().sidebar
52456                             .show(errorEditor.error(error));
52457                     });
52458                     break;
52459                 case 'osmose':
52460                     errorEditor = uiOsmoseEditor(context)
52461                     .on('change', function() {
52462                         context.map().pan([0,0]);  // trigger a redraw
52463                         var error = checkSelectedID();
52464                         if (!error) { return; }
52465                         context.ui().sidebar
52466                             .show(errorEditor.error(error));
52467                     });
52468                     break;
52469             }
52470
52471
52472             var behaviors = [
52473                 behaviorBreathe(),
52474                 behaviorHover(context),
52475                 behaviorSelect(context),
52476                 behaviorLasso(context),
52477                 modeDragNode(context).behavior,
52478                 modeDragNote(context).behavior
52479             ];
52480
52481
52482             function checkSelectedID() {
52483                 if (!errorService) { return; }
52484                 var error = errorService.getError(selectedErrorID);
52485                 if (!error) {
52486                     context.enter(modeBrowse(context));
52487                 }
52488                 return error;
52489             }
52490
52491
52492             mode.zoomToSelected = function() {
52493                 if (!errorService) { return; }
52494                 var error = errorService.getError(selectedErrorID);
52495                 if (error) {
52496                     context.map().centerZoomEase(error.loc, 20);
52497                 }
52498             };
52499
52500
52501             mode.enter = function() {
52502                 var error = checkSelectedID();
52503                 if (!error) { return; }
52504
52505                 behaviors.forEach(context.install);
52506                 keybinding
52507                     .on(_t('inspector.zoom_to.key'), mode.zoomToSelected)
52508                     .on('⎋', esc, true);
52509
52510                 select(document)
52511                     .call(keybinding);
52512
52513                 selectError();
52514
52515                 var sidebar = context.ui().sidebar;
52516                 sidebar.show(errorEditor.error(error));
52517
52518                 context.map()
52519                     .on('drawn.select-error', selectError);
52520
52521
52522                 // class the error as selected, or return to browse mode if the error is gone
52523                 function selectError(drawn) {
52524                     if (!checkSelectedID()) { return; }
52525
52526                     var selection = context.surface()
52527                         .selectAll('.itemId-' + selectedErrorID + '.' + selectedErrorService);
52528
52529                     if (selection.empty()) {
52530                         // Return to browse mode if selected DOM elements have
52531                         // disappeared because the user moved them out of view..
52532                         var source = event && event.type === 'zoom' && event.sourceEvent;
52533                         if (drawn && source && (source.type === 'pointermove' || source.type === 'mousemove' || source.type === 'touchmove')) {
52534                             context.enter(modeBrowse(context));
52535                         }
52536
52537                     } else {
52538                         selection
52539                             .classed('selected', true);
52540
52541                         context.selectedErrorID(selectedErrorID);
52542                     }
52543                 }
52544
52545                 function esc() {
52546                     if (context.container().select('.combobox').size()) { return; }
52547                     context.enter(modeBrowse(context));
52548                 }
52549             };
52550
52551
52552             mode.exit = function() {
52553                 behaviors.forEach(context.uninstall);
52554
52555                 select(document)
52556                     .call(keybinding.unbind);
52557
52558                 context.surface()
52559                     .selectAll('.qaItem.selected')
52560                     .classed('selected hover', false);
52561
52562                 context.map()
52563                     .on('drawn.select-error', null);
52564
52565                 context.ui().sidebar
52566                     .hide();
52567
52568                 context.selectedErrorID(null);
52569                 context.features().forceVisible([]);
52570             };
52571
52572
52573             return mode;
52574         }
52575
52576         function behaviorSelect(context) {
52577             var _tolerancePx = 4; // see also behaviorDrag
52578             var _lastMouseEvent = null;
52579             var _showMenu = false;
52580             var _downPointers = {};
52581             var _longPressTimeout = null;
52582             var _lastInteractionType = null;
52583             // the id of the down pointer that's enabling multiselection while down
52584             var _multiselectionPointerId = null;
52585
52586             // use pointer events on supported platforms; fallback to mouse events
52587             var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
52588
52589
52590             function keydown() {
52591
52592                 if (event.keyCode === 32) {
52593                     // don't react to spacebar events during text input
52594                     var activeNode = document.activeElement;
52595                     if (activeNode && new Set(['INPUT', 'TEXTAREA']).has(activeNode.nodeName)) { return; }
52596                 }
52597
52598                 if (event.keyCode === 93 ||  // context menu key
52599                     event.keyCode === 32) {  // spacebar
52600                     event.preventDefault();
52601                 }
52602
52603                 if (event.repeat) { return; } // ignore repeated events for held keys
52604
52605                 // if any key is pressed the user is probably doing something other than long-pressing
52606                 cancelLongPress();
52607
52608                 if (event.shiftKey) {
52609                     context.surface()
52610                         .classed('behavior-multiselect', true);
52611                 }
52612
52613                 if (event.keyCode === 32) {  // spacebar
52614                     if (!_downPointers.spacebar && _lastMouseEvent) {
52615                         cancelLongPress();
52616                         _longPressTimeout = window.setTimeout(didLongPress, 500, 'spacebar', 'spacebar');
52617
52618                         _downPointers.spacebar = {
52619                             firstEvent: _lastMouseEvent,
52620                             lastEvent: _lastMouseEvent
52621                         };
52622                     }
52623                 }
52624             }
52625
52626
52627             function keyup() {
52628                 cancelLongPress();
52629
52630                 if (!event.shiftKey) {
52631                     context.surface()
52632                         .classed('behavior-multiselect', false);
52633                 }
52634
52635                 if (event.keyCode === 93) {  // context menu key
52636                     event.preventDefault();
52637                     _lastInteractionType = 'menukey';
52638                     contextmenu();
52639                 } else if (event.keyCode === 32) {  // spacebar
52640                     var pointer = _downPointers.spacebar;
52641                     if (pointer) {
52642                         delete _downPointers.spacebar;
52643
52644                         if (pointer.done) { return; }
52645
52646                         event.preventDefault();
52647                         _lastInteractionType = 'spacebar';
52648                         click(pointer.firstEvent, pointer.lastEvent, 'spacebar');
52649                     }
52650                 }
52651             }
52652
52653
52654             function pointerdown() {
52655                 var id = (event.pointerId || 'mouse').toString();
52656
52657                 cancelLongPress();
52658
52659                 if (event.buttons && event.buttons !== 1) { return; }
52660
52661                 context.ui().closeEditMenu();
52662
52663                 _longPressTimeout = window.setTimeout(didLongPress, 500, id, 'longdown-' + (event.pointerType || 'mouse'));
52664
52665                 _downPointers[id] = {
52666                     firstEvent: event,
52667                     lastEvent: event
52668                 };
52669             }
52670
52671
52672             function didLongPress(id, interactionType) {
52673                 var pointer = _downPointers[id];
52674                 if (!pointer) { return; }
52675
52676                 for (var i in _downPointers) {
52677                     // don't allow this or any currently down pointer to trigger another click
52678                     _downPointers[i].done = true;
52679                 }
52680
52681                 // treat long presses like right-clicks
52682                 _longPressTimeout = null;
52683                 _lastInteractionType = interactionType;
52684                 _showMenu = true;
52685
52686                 click(pointer.firstEvent, pointer.lastEvent, id);
52687             }
52688
52689
52690             function pointermove() {
52691                 var id = (event.pointerId || 'mouse').toString();
52692                 if (_downPointers[id]) {
52693                     _downPointers[id].lastEvent = event;
52694                 }
52695                 if (!event.pointerType || event.pointerType === 'mouse') {
52696                     _lastMouseEvent = event;
52697                     if (_downPointers.spacebar) {
52698                         _downPointers.spacebar.lastEvent = event;
52699                     }
52700                 }
52701             }
52702
52703
52704             function pointerup() {
52705                 var id = (event.pointerId || 'mouse').toString();
52706                 var pointer = _downPointers[id];
52707                 if (!pointer) { return; }
52708
52709                 delete _downPointers[id];
52710
52711                 if (_multiselectionPointerId === id) {
52712                     _multiselectionPointerId = null;
52713                 }
52714
52715                 if (pointer.done) { return; }
52716
52717                 click(pointer.firstEvent, event, id);
52718             }
52719
52720
52721             function pointercancel() {
52722                 var id = (event.pointerId || 'mouse').toString();
52723                 if (!_downPointers[id]) { return; }
52724
52725                 delete _downPointers[id];
52726
52727                 if (_multiselectionPointerId === id) {
52728                     _multiselectionPointerId = null;
52729                 }
52730             }
52731
52732
52733             function contextmenu() {
52734                 var e = event;
52735                 e.preventDefault();
52736
52737                 if (!+e.clientX && !+e.clientY) {
52738                     if (_lastMouseEvent) {
52739                         e.sourceEvent = _lastMouseEvent;
52740                     } else {
52741                         return;
52742                     }
52743                 } else {
52744                     _lastMouseEvent = event;
52745                     _lastInteractionType = 'rightclick';
52746                 }
52747
52748                 _showMenu = true;
52749                 click(event, event);
52750             }
52751
52752
52753             function click(firstEvent, lastEvent, pointerId) {
52754                 cancelLongPress();
52755
52756                 var mapNode = context.container().select('.main-map').node();
52757
52758                 // Use the `main-map` coordinate system since the surface and supersurface
52759                 // are transformed when drag-panning.
52760                 var pointGetter = utilFastMouse(mapNode);
52761                 var p1 = pointGetter(firstEvent);
52762                 var p2 = pointGetter(lastEvent);
52763                 var dist = geoVecLength(p1, p2);
52764
52765                 if (dist > _tolerancePx ||
52766                     !mapContains(lastEvent)) {
52767
52768                     resetProperties();
52769                     return;
52770                 }
52771
52772                 var targetDatum = lastEvent.target.__data__;
52773
52774                 var multiselectEntityId;
52775
52776                 if (!_multiselectionPointerId) {
52777                     // If a different pointer than the one triggering this click is down on a
52778                     // feature, treat this and all future clicks as multiselection until that
52779                     // pointer is raised.
52780                     var selectPointerInfo = pointerDownOnSelection(pointerId);
52781                     if (selectPointerInfo) {
52782                         _multiselectionPointerId = selectPointerInfo.pointerId;
52783                         // if the other feature isn't selected yet, make sure we select it
52784                         multiselectEntityId = !selectPointerInfo.selected && selectPointerInfo.entityId;
52785                         _downPointers[selectPointerInfo.pointerId].done = true;
52786                     }
52787                 }
52788
52789                 // support multiselect if data is already selected
52790                 var isMultiselect = context.mode().id === 'select' && (
52791                     // and shift key is down
52792                     (event && event.shiftKey) ||
52793                     // or we're lasso-selecting
52794                     context.surface().select('.lasso').node() ||
52795                     // or a pointer is down over a selected feature
52796                     (_multiselectionPointerId && !multiselectEntityId)
52797                 );
52798
52799                 processClick(targetDatum, isMultiselect, p2, multiselectEntityId);
52800
52801                 function mapContains(event) {
52802                     var rect = mapNode.getBoundingClientRect();
52803                     return event.clientX >= rect.left &&
52804                         event.clientX <= rect.right &&
52805                         event.clientY >= rect.top &&
52806                         event.clientY <= rect.bottom;
52807                 }
52808
52809                 function pointerDownOnSelection(skipPointerId) {
52810                     var mode = context.mode();
52811                     var selectedIDs = mode.id === 'select' ? mode.selectedIDs() : [];
52812                     for (var pointerId in _downPointers) {
52813                         if (pointerId === 'spacebar' || pointerId === skipPointerId) { continue; }
52814
52815                         var pointerInfo = _downPointers[pointerId];
52816
52817                         var p1 = pointGetter(pointerInfo.firstEvent);
52818                         var p2 = pointGetter(pointerInfo.lastEvent);
52819                         if (geoVecLength(p1, p2) > _tolerancePx) { continue; }
52820
52821                         var datum = pointerInfo.firstEvent.target.__data__;
52822                         var entity = (datum && datum.properties && datum.properties.entity) || datum;
52823                         if (context.graph().hasEntity(entity.id)) { return {
52824                             pointerId: pointerId,
52825                             entityId: entity.id,
52826                             selected: selectedIDs.indexOf(entity.id) !== -1
52827                         }; }
52828                     }
52829                     return null;
52830                 }
52831             }
52832
52833
52834             function processClick(datum, isMultiselect, point, alsoSelectId) {
52835                 var mode = context.mode();
52836                 var showMenu = _showMenu;
52837                 var interactionType = _lastInteractionType;
52838
52839                 var entity = datum && datum.properties && datum.properties.entity;
52840                 if (entity) { datum = entity; }
52841
52842                 if (datum && datum.type === 'midpoint') {
52843                     // treat targeting midpoints as if targeting the parent way
52844                     datum = datum.parents[0];
52845                 }
52846
52847                 var newMode;
52848
52849                 if (datum instanceof osmEntity) {
52850                     // targeting an entity
52851                     var selectedIDs = context.selectedIDs();
52852                     context.selectedNoteID(null);
52853                     context.selectedErrorID(null);
52854
52855                     if (!isMultiselect) {
52856                         // don't change the selection if we're toggling the menu atop a multiselection
52857                         if (!showMenu ||
52858                             selectedIDs.length <= 1 ||
52859                             selectedIDs.indexOf(datum.id) === -1) {
52860
52861                             if (alsoSelectId === datum.id) { alsoSelectId = null; }
52862
52863                             selectedIDs = (alsoSelectId ? [alsoSelectId] : []).concat([datum.id]);
52864                             // always enter modeSelect even if the entity is already
52865                             // selected since listeners may expect `context.enter` events,
52866                             // e.g. in the walkthrough
52867                             newMode = mode.id === 'select' ? mode.selectedIDs(selectedIDs) : modeSelect(context, selectedIDs).selectBehavior(behavior);
52868                             context.enter(newMode);
52869                         }
52870
52871                     } else {
52872                         if (selectedIDs.indexOf(datum.id) !== -1) {
52873                             // clicked entity is already in the selectedIDs list..
52874                             if (!showMenu) {
52875                                 // deselect clicked entity, then reenter select mode or return to browse mode..
52876                                 selectedIDs = selectedIDs.filter(function(id) { return id !== datum.id; });
52877                                 newMode = selectedIDs.length ? mode.selectedIDs(selectedIDs) : modeBrowse(context).selectBehavior(behavior);
52878                                 context.enter(newMode);
52879                             }
52880                         } else {
52881                             // clicked entity is not in the selected list, add it..
52882                             selectedIDs = selectedIDs.concat([datum.id]);
52883                             newMode = mode.selectedIDs(selectedIDs);
52884                             context.enter(newMode);
52885                         }
52886                     }
52887
52888                 } else if (datum && datum.__featurehash__ && !isMultiselect) {
52889                     // targeting custom data
52890                     context
52891                         .selectedNoteID(null)
52892                         .enter(modeSelectData(context, datum));
52893
52894                 } else if (datum instanceof osmNote && !isMultiselect) {
52895                     // targeting a note
52896                     context
52897                         .selectedNoteID(datum.id)
52898                         .enter(modeSelectNote(context, datum.id));
52899
52900                 } else if (datum instanceof QAItem & !isMultiselect) {
52901                     // targeting an external QA issue
52902                     context
52903                         .selectedErrorID(datum.id)
52904                         .enter(modeSelectError(context, datum.id, datum.service));
52905
52906                 } else {
52907                     // targeting nothing
52908                     context.selectedNoteID(null);
52909                     context.selectedErrorID(null);
52910                     if (!isMultiselect && mode.id !== 'browse') {
52911                         context.enter(modeBrowse(context));
52912                     }
52913                 }
52914
52915                 context.ui().closeEditMenu();
52916
52917                 // always request to show the edit menu in case the mode needs it
52918                 if (showMenu) { context.ui().showEditMenu(point, interactionType); }
52919
52920                 resetProperties();
52921             }
52922
52923
52924             function cancelLongPress() {
52925                 if (_longPressTimeout) { window.clearTimeout(_longPressTimeout); }
52926                 _longPressTimeout = null;
52927             }
52928
52929
52930             function resetProperties() {
52931                 cancelLongPress();
52932                 _showMenu = false;
52933                 _lastInteractionType = null;
52934                 // don't reset _lastMouseEvent since it might still be useful
52935             }
52936
52937
52938             function behavior(selection) {
52939                 resetProperties();
52940                 _lastMouseEvent = context.map().lastPointerEvent();
52941
52942                 select(window)
52943                     .on('keydown.select', keydown)
52944                     .on('keyup.select', keyup)
52945                     .on(_pointerPrefix + 'move.select', pointermove, true)
52946                     .on(_pointerPrefix + 'up.select', pointerup, true)
52947                     .on('pointercancel.select', pointercancel, true)
52948                     .on('contextmenu.select-window', function() {
52949                         // Edge and IE really like to show the contextmenu on the
52950                         // menubar when user presses a keyboard menu button
52951                         // even after we've already preventdefaulted the key event.
52952                         var e = event;
52953                         if (+e.clientX === 0 && +e.clientY === 0) {
52954                             event.preventDefault();
52955                         }
52956                     });
52957
52958                 selection
52959                     .on(_pointerPrefix + 'down.select', pointerdown)
52960                     .on('contextmenu.select', contextmenu);
52961
52962                 if (event && event.shiftKey) {
52963                     context.surface()
52964                         .classed('behavior-multiselect', true);
52965                 }
52966             }
52967
52968
52969             behavior.off = function(selection) {
52970                 cancelLongPress();
52971
52972                 select(window)
52973                     .on('keydown.select', null)
52974                     .on('keyup.select', null)
52975                     .on('contextmenu.select-window', null)
52976                     .on(_pointerPrefix + 'move.select', null, true)
52977                     .on(_pointerPrefix + 'up.select', null, true)
52978                     .on('pointercancel.select', null, true);
52979
52980                 selection
52981                     .on(_pointerPrefix + 'down.select', null)
52982                     .on('contextmenu.select', null);
52983
52984                 context.surface()
52985                     .classed('behavior-multiselect', false);
52986             };
52987
52988
52989             return behavior;
52990         }
52991
52992         function behaviorDrawWay(context, wayID, mode, startGraph) {
52993
52994             var dispatch$1 = dispatch('rejectedSelfIntersection');
52995
52996             var behavior = behaviorDraw(context);
52997
52998             // Must be set by `drawWay.nodeIndex` before each install of this behavior.
52999             var _nodeIndex;
53000
53001             var _origWay;
53002             var _wayGeometry;
53003             var _headNodeID;
53004             var _annotation;
53005
53006             var _pointerHasMoved = false;
53007
53008             // The osmNode to be placed.
53009             // This is temporary and just follows the mouse cursor until an "add" event occurs.
53010             var _drawNode;
53011
53012             var _didResolveTempEdit = false;
53013
53014             function createDrawNode(loc) {
53015                 // don't make the draw node until we actually need it
53016                 _drawNode = osmNode({ loc: loc });
53017
53018                 context.pauseChangeDispatch();
53019                 context.replace(function actionAddDrawNode(graph) {
53020                     // add the draw node to the graph and insert it into the way
53021                     var way = graph.entity(wayID);
53022                     return graph
53023                         .replace(_drawNode)
53024                         .replace(way.addNode(_drawNode.id, _nodeIndex));
53025                 }, _annotation);
53026                 context.resumeChangeDispatch();
53027
53028                 setActiveElements();
53029             }
53030
53031             function removeDrawNode() {
53032
53033                 context.pauseChangeDispatch();
53034                 context.replace(
53035                     function actionDeleteDrawNode(graph) {
53036                        var way = graph.entity(wayID);
53037                        return graph
53038                            .replace(way.removeNode(_drawNode.id))
53039                            .remove(_drawNode);
53040                    },
53041                     _annotation
53042                 );
53043                 _drawNode = undefined;
53044                 context.resumeChangeDispatch();
53045             }
53046
53047
53048             function keydown() {
53049                 if (event.keyCode === utilKeybinding.modifierCodes.alt) {
53050                     if (context.surface().classed('nope')) {
53051                         context.surface()
53052                             .classed('nope-suppressed', true);
53053                     }
53054                     context.surface()
53055                         .classed('nope', false)
53056                         .classed('nope-disabled', true);
53057                 }
53058             }
53059
53060
53061             function keyup() {
53062                 if (event.keyCode === utilKeybinding.modifierCodes.alt) {
53063                     if (context.surface().classed('nope-suppressed')) {
53064                         context.surface()
53065                             .classed('nope', true);
53066                     }
53067                     context.surface()
53068                         .classed('nope-suppressed', false)
53069                         .classed('nope-disabled', false);
53070                 }
53071             }
53072
53073
53074             function allowsVertex(d) {
53075                 return d.geometry(context.graph()) === 'vertex' || _mainPresetIndex.allowsVertex(d, context.graph());
53076             }
53077
53078
53079             // related code
53080             // - `mode/drag_node.js`     `doMove()`
53081             // - `behavior/draw.js`      `click()`
53082             // - `behavior/draw_way.js`  `move()`
53083             function move(datum) {
53084
53085                 var loc = context.map().mouseCoordinates();
53086
53087                 if (!_drawNode) { createDrawNode(loc); }
53088
53089                 context.surface().classed('nope-disabled', event.altKey);
53090
53091                 var targetLoc = datum && datum.properties && datum.properties.entity &&
53092                     allowsVertex(datum.properties.entity) && datum.properties.entity.loc;
53093                 var targetNodes = datum && datum.properties && datum.properties.nodes;
53094
53095                 if (targetLoc) {   // snap to node/vertex - a point target with `.loc`
53096                     loc = targetLoc;
53097
53098                 } else if (targetNodes) {   // snap to way - a line target with `.nodes`
53099                     var choice = geoChooseEdge(targetNodes, context.map().mouse(), context.projection, _drawNode.id);
53100                     if (choice) {
53101                         loc = choice.loc;
53102                     }
53103                 }
53104
53105                 context.replace(actionMoveNode(_drawNode.id, loc), _annotation);
53106                 _drawNode = context.entity(_drawNode.id);
53107                 checkGeometry(true /* includeDrawNode */);
53108             }
53109
53110
53111             // Check whether this edit causes the geometry to break.
53112             // If so, class the surface with a nope cursor.
53113             // `includeDrawNode` - Only check the relevant line segments if finishing drawing
53114             function checkGeometry(includeDrawNode) {
53115                 var nopeDisabled = context.surface().classed('nope-disabled');
53116                 var isInvalid = isInvalidGeometry(includeDrawNode);
53117
53118                 if (nopeDisabled) {
53119                     context.surface()
53120                         .classed('nope', false)
53121                         .classed('nope-suppressed', isInvalid);
53122                 } else {
53123                     context.surface()
53124                         .classed('nope', isInvalid)
53125                         .classed('nope-suppressed', false);
53126                 }
53127             }
53128
53129
53130             function isInvalidGeometry(includeDrawNode) {
53131
53132                 var testNode = _drawNode;
53133
53134                 // we only need to test the single way we're drawing
53135                 var parentWay = context.graph().entity(wayID);
53136                 var nodes = context.graph().childNodes(parentWay).slice();  // shallow copy
53137
53138                 if (includeDrawNode) {
53139                     if (parentWay.isClosed()) {
53140                         // don't test the last segment for closed ways - #4655
53141                         // (still test the first segement)
53142                         nodes.pop();
53143                     }
53144                 } else { // discount the draw node
53145
53146                     if (parentWay.isClosed()) {
53147                         if (nodes.length < 3) { return false; }
53148                         if (_drawNode) { nodes.splice(-2, 1); }
53149                         testNode = nodes[nodes.length - 2];
53150                     } else {
53151                         // there's nothing we need to test if we ignore the draw node on open ways
53152                         return false;
53153                     }
53154                 }
53155
53156                 return testNode && geoHasSelfIntersections(nodes, testNode.id);
53157             }
53158
53159
53160             function undone() {
53161
53162                 // undoing removed the temp edit
53163                 _didResolveTempEdit = true;
53164
53165                 context.pauseChangeDispatch();
53166
53167                 var nextMode;
53168
53169                 if (context.graph() === startGraph) { // we've undone back to the beginning
53170                     nextMode = modeSelect(context, [wayID]);
53171                 } else {
53172                     context.history()
53173                         .on('undone.draw', null);
53174                     // remove whatever segment was drawn previously
53175                     context.undo();
53176
53177                     if (context.graph() === startGraph) { // we've undone back to the beginning
53178                         nextMode = modeSelect(context, [wayID]);
53179                     } else {
53180                         // continue drawing
53181                         nextMode = mode;
53182                     }
53183                 }
53184
53185                 // clear the redo stack by adding and removing an edit
53186                 context.perform(actionNoop());
53187                 context.pop(1);
53188
53189                 context.resumeChangeDispatch();
53190                 context.enter(nextMode);
53191             }
53192
53193
53194             function setActiveElements() {
53195                 if (!_drawNode) { return; }
53196
53197                 context.surface().selectAll('.' + _drawNode.id)
53198                     .classed('active', true);
53199             }
53200
53201
53202             function resetToStartGraph() {
53203                 while (context.graph() !== startGraph) {
53204                     context.pop();
53205                 }
53206             }
53207
53208
53209             var drawWay = function(surface) {
53210                 _drawNode = undefined;
53211                 _didResolveTempEdit = false;
53212                 _origWay = context.entity(wayID);
53213                 _headNodeID = typeof _nodeIndex === 'number' ? _origWay.nodes[_nodeIndex] :
53214                     (_origWay.isClosed() ? _origWay.nodes[_origWay.nodes.length - 2] : _origWay.nodes[_origWay.nodes.length - 1]);
53215                 _wayGeometry = _origWay.geometry(context.graph());
53216                 _annotation = _t((_origWay.isDegenerate() ?
53217                     'operations.start.annotation.' :
53218                     'operations.continue.annotation.') + _wayGeometry
53219                 );
53220                 _pointerHasMoved = false;
53221
53222                 // Push an annotated state for undo to return back to.
53223                 // We must make sure to replace or remove it later.
53224                 context.pauseChangeDispatch();
53225                 context.perform(actionNoop(), _annotation);
53226                 context.resumeChangeDispatch();
53227
53228                 behavior.hover()
53229                     .initialNodeID(_headNodeID);
53230
53231                 behavior
53232                     .on('move', function() {
53233                         _pointerHasMoved = true;
53234                         move.apply(this, arguments);
53235                     })
53236                     .on('down', function() {
53237                         move.apply(this, arguments);
53238                     })
53239                     .on('downcancel', function() {
53240                         if (_drawNode) { removeDrawNode(); }
53241                     })
53242                     .on('click', drawWay.add)
53243                     .on('clickWay', drawWay.addWay)
53244                     .on('clickNode', drawWay.addNode)
53245                     .on('undo', context.undo)
53246                     .on('cancel', drawWay.cancel)
53247                     .on('finish', drawWay.finish);
53248
53249                 select(window)
53250                     .on('keydown.drawWay', keydown)
53251                     .on('keyup.drawWay', keyup);
53252
53253                 context.map()
53254                     .dblclickZoomEnable(false)
53255                     .on('drawn.draw', setActiveElements);
53256
53257                 setActiveElements();
53258
53259                 surface.call(behavior);
53260
53261                 context.history()
53262                     .on('undone.draw', undone);
53263             };
53264
53265
53266             drawWay.off = function(surface) {
53267
53268                 if (!_didResolveTempEdit) {
53269                     // Drawing was interrupted unexpectedly.
53270                     // This can happen if the user changes modes,
53271                     // clicks geolocate button, a hashchange event occurs, etc.
53272
53273                     context.pauseChangeDispatch();
53274                     resetToStartGraph();
53275                     context.resumeChangeDispatch();
53276                 }
53277
53278                 _drawNode = undefined;
53279                 _nodeIndex = undefined;
53280
53281                 context.map()
53282                     .on('drawn.draw', null);
53283
53284                 surface.call(behavior.off)
53285                     .selectAll('.active')
53286                     .classed('active', false);
53287
53288                 surface
53289                     .classed('nope', false)
53290                     .classed('nope-suppressed', false)
53291                     .classed('nope-disabled', false);
53292
53293                 select(window)
53294                     .on('keydown.drawWay', null)
53295                     .on('keyup.drawWay', null);
53296
53297                 context.history()
53298                     .on('undone.draw', null);
53299             };
53300
53301
53302             function attemptAdd(d, loc, doAdd) {
53303
53304                 if (_drawNode) {
53305                     // move the node to the final loc in case move wasn't called
53306                     // consistently (e.g. on touch devices)
53307                     context.replace(actionMoveNode(_drawNode.id, loc), _annotation);
53308                     _drawNode = context.entity(_drawNode.id);
53309                 } else {
53310                     createDrawNode(loc);
53311                 }
53312
53313                 checkGeometry(true /* includeDrawNode */);
53314                 if ((d && d.properties && d.properties.nope) || context.surface().classed('nope')) {
53315                     if (!_pointerHasMoved) {
53316                         // prevent the temporary draw node from appearing on touch devices
53317                         removeDrawNode();
53318                     }
53319                     dispatch$1.call('rejectedSelfIntersection', this);
53320                     return;   // can't click here
53321                 }
53322
53323                 context.pauseChangeDispatch();
53324                 doAdd();
53325                 // we just replaced the temporary edit with the real one
53326                 _didResolveTempEdit = true;
53327                 context.resumeChangeDispatch();
53328
53329                 context.enter(mode);
53330             }
53331
53332
53333             // Accept the current position of the drawing node
53334             drawWay.add = function(loc, d) {
53335                 attemptAdd(d, loc, function() {
53336                     // don't need to do anything extra
53337                 });
53338             };
53339
53340
53341             // Connect the way to an existing way
53342             drawWay.addWay = function(loc, edge, d) {
53343                 attemptAdd(d, loc, function() {
53344                     context.replace(
53345                         actionAddMidpoint({ loc: loc, edge: edge }, _drawNode),
53346                         _annotation
53347                     );
53348                 });
53349             };
53350
53351
53352             // Connect the way to an existing node
53353             drawWay.addNode = function(node, d) {
53354
53355                 // finish drawing if the mapper targets the prior node
53356                 if (node.id === _headNodeID ||
53357                     // or the first node when drawing an area
53358                     (_origWay.isClosed() && node.id === _origWay.first())) {
53359                     drawWay.finish();
53360                     return;
53361                 }
53362
53363                 attemptAdd(d, node.loc, function() {
53364                     context.replace(
53365                         function actionReplaceDrawNode(graph) {
53366                             // remove the temporary draw node and insert the existing node
53367                             // at the same index
53368
53369                             graph = graph
53370                                 .replace(graph.entity(wayID).removeNode(_drawNode.id))
53371                                 .remove(_drawNode);
53372                             return graph
53373                                 .replace(graph.entity(wayID).addNode(node.id, _nodeIndex));
53374                         },
53375                         _annotation
53376                     );
53377                 });
53378             };
53379
53380
53381             // Finish the draw operation, removing the temporary edit.
53382             // If the way has enough nodes to be valid, it's selected.
53383             // Otherwise, delete everything and return to browse mode.
53384             drawWay.finish = function() {
53385                 checkGeometry(false /* includeDrawNode */);
53386                 if (context.surface().classed('nope')) {
53387                     dispatch$1.call('rejectedSelfIntersection', this);
53388                     return;   // can't click here
53389                 }
53390
53391                 context.pauseChangeDispatch();
53392                 // remove the temporary edit
53393                 context.pop(1);
53394                 _didResolveTempEdit = true;
53395                 context.resumeChangeDispatch();
53396
53397                 var way = context.hasEntity(wayID);
53398                 if (!way || way.isDegenerate()) {
53399                     drawWay.cancel();
53400                     return;
53401                 }
53402
53403                 window.setTimeout(function() {
53404                     context.map().dblclickZoomEnable(true);
53405                 }, 1000);
53406
53407                 var isNewFeature = !mode.isContinuing;
53408                 context.enter(modeSelect(context, [wayID]).newFeature(isNewFeature));
53409             };
53410
53411
53412             // Cancel the draw operation, delete everything, and return to browse mode.
53413             drawWay.cancel = function() {
53414                 context.pauseChangeDispatch();
53415                 resetToStartGraph();
53416                 context.resumeChangeDispatch();
53417
53418                 window.setTimeout(function() {
53419                     context.map().dblclickZoomEnable(true);
53420                 }, 1000);
53421
53422                 context.surface()
53423                     .classed('nope', false)
53424                     .classed('nope-disabled', false)
53425                     .classed('nope-suppressed', false);
53426
53427                 context.enter(modeBrowse(context));
53428             };
53429
53430
53431             drawWay.nodeIndex = function(val) {
53432                 if (!arguments.length) { return _nodeIndex; }
53433                 _nodeIndex = val;
53434                 return drawWay;
53435             };
53436
53437
53438             drawWay.activeID = function() {
53439                 if (!arguments.length) { return _drawNode && _drawNode.id; }
53440                 // no assign
53441                 return drawWay;
53442             };
53443
53444
53445             return utilRebind(drawWay, dispatch$1, 'on');
53446         }
53447
53448         function modeDrawLine(context, wayID, startGraph, button, affix, continuing) {
53449             var mode = {
53450                 button: button,
53451                 id: 'draw-line'
53452             };
53453
53454             var behavior = behaviorDrawWay(context, wayID, mode, startGraph)
53455                 .on('rejectedSelfIntersection.modeDrawLine', function() {
53456                     context.ui().flash
53457                         .text(_t('self_intersection.error.lines'))();
53458                 });
53459
53460             mode.wayID = wayID;
53461
53462             mode.isContinuing = continuing;
53463
53464             mode.enter = function() {
53465                 behavior
53466                     .nodeIndex(affix === 'prefix' ? 0 : undefined);
53467
53468                 context.install(behavior);
53469             };
53470
53471             mode.exit = function() {
53472                 context.uninstall(behavior);
53473             };
53474
53475             mode.selectedIDs = function() {
53476                 return [wayID];
53477             };
53478
53479             mode.activeID = function() {
53480                 return (behavior && behavior.activeID()) || [];
53481             };
53482
53483             return mode;
53484         }
53485
53486         function operationContinue(context, selectedIDs) {
53487             var graph = context.graph();
53488             var entities = selectedIDs.map(function(id) { return graph.entity(id); });
53489             var geometries = Object.assign(
53490                 { line: [], vertex: [] },
53491                 utilArrayGroupBy(entities, function(entity) { return entity.geometry(graph); })
53492             );
53493             var vertex = geometries.vertex[0];
53494
53495
53496             function candidateWays() {
53497                 return graph.parentWays(vertex).filter(function(parent) {
53498                     return parent.geometry(graph) === 'line' &&
53499                         !parent.isClosed() &&
53500                         parent.affix(vertex.id) &&
53501                         (geometries.line.length === 0 || geometries.line[0] === parent);
53502                 });
53503             }
53504
53505
53506             var operation = function() {
53507                 var candidate = candidateWays()[0];
53508                 context.enter(
53509                     modeDrawLine(context, candidate.id, context.graph(), 'line', candidate.affix(vertex.id), true)
53510                 );
53511             };
53512
53513
53514             operation.available = function() {
53515                 return geometries.vertex.length === 1 &&
53516                     geometries.line.length <= 1 &&
53517                     !context.features().hasHiddenConnections(vertex, context.graph());
53518             };
53519
53520
53521             operation.disabled = function() {
53522                 var candidates = candidateWays();
53523                 if (candidates.length === 0) {
53524                     return 'not_eligible';
53525                 } else if (candidates.length > 1) {
53526                     return 'multiple';
53527                 }
53528
53529                 return false;
53530             };
53531
53532
53533             operation.tooltip = function() {
53534                 var disable = operation.disabled();
53535                 return disable ?
53536                     _t('operations.continue.' + disable) :
53537                     _t('operations.continue.description');
53538             };
53539
53540
53541             operation.annotation = function() {
53542                 return _t('operations.continue.annotation.line');
53543             };
53544
53545
53546             operation.id = 'continue';
53547             operation.keys = [_t('operations.continue.key')];
53548             operation.title = _t('operations.continue.title');
53549             operation.behavior = behaviorOperation(context).which(operation);
53550
53551             return operation;
53552         }
53553
53554         function operationCopy(context, selectedIDs) {
53555
53556             var _multi = selectedIDs.length === 1 ? 'single' : 'multiple';
53557
53558             function getFilteredIdsToCopy() {
53559                 return selectedIDs.filter(function(selectedID) {
53560                     var entity = context.graph().hasEntity(selectedID);
53561                     // don't copy untagged vertices separately from ways
53562                     return entity.hasInterestingTags() || entity.geometry(context.graph()) !== 'vertex';
53563                 });
53564             }
53565
53566             var operation = function() {
53567
53568                 if (!getSelectionText()) {
53569                     event.preventDefault();
53570                 }
53571
53572                 var graph = context.graph();
53573                 var selected = groupEntities(getFilteredIdsToCopy(), graph);
53574                 var canCopy = [];
53575                 var skip = {};
53576                 var entity;
53577                 var i;
53578
53579                 for (i = 0; i < selected.relation.length; i++) {
53580                     entity = selected.relation[i];
53581                     if (!skip[entity.id] && entity.isComplete(graph)) {
53582                         canCopy.push(entity.id);
53583                         skip = getDescendants(entity.id, graph, skip);
53584                     }
53585                 }
53586                 for (i = 0; i < selected.way.length; i++) {
53587                     entity = selected.way[i];
53588                     if (!skip[entity.id]) {
53589                         canCopy.push(entity.id);
53590                         skip = getDescendants(entity.id, graph, skip);
53591                     }
53592                 }
53593                 for (i = 0; i < selected.node.length; i++) {
53594                     entity = selected.node[i];
53595                     if (!skip[entity.id]) {
53596                         canCopy.push(entity.id);
53597                     }
53598                 }
53599
53600                 context.copyIDs(canCopy);
53601                 if (_point &&
53602                     (canCopy.length !== 1 || graph.entity(canCopy[0]).type !== 'node')) {
53603                     // store the anchor coordinates if copying more than a single node
53604                     context.copyLonLat(context.projection.invert(_point));
53605                 } else {
53606                     context.copyLonLat(null);
53607                 }
53608
53609             };
53610
53611
53612             function groupEntities(ids, graph) {
53613                 var entities = ids.map(function (id) { return graph.entity(id); });
53614                 return Object.assign(
53615                     { relation: [], way: [], node: [] },
53616                     utilArrayGroupBy(entities, 'type')
53617                 );
53618             }
53619
53620
53621             function getDescendants(id, graph, descendants) {
53622                 var entity = graph.entity(id);
53623                 var children;
53624
53625                 descendants = descendants || {};
53626
53627                 if (entity.type === 'relation') {
53628                     children = entity.members.map(function(m) { return m.id; });
53629                 } else if (entity.type === 'way') {
53630                     children = entity.nodes;
53631                 } else {
53632                     children = [];
53633                 }
53634
53635                 for (var i = 0; i < children.length; i++) {
53636                     if (!descendants[children[i]]) {
53637                         descendants[children[i]] = true;
53638                         descendants = getDescendants(children[i], graph, descendants);
53639                     }
53640                 }
53641
53642                 return descendants;
53643             }
53644
53645
53646             function getSelectionText() {
53647                 return window.getSelection().toString();
53648             }
53649
53650
53651             operation.available = function() {
53652                 return getFilteredIdsToCopy().length > 0;
53653             };
53654
53655
53656             operation.disabled = function() {
53657                 var extent = utilTotalExtent(getFilteredIdsToCopy(), context.graph());
53658                 if (extent.percentContainedIn(context.map().extent()) < 0.8) {
53659                     return 'too_large';
53660                 }
53661                 return false;
53662             };
53663
53664
53665             operation.tooltip = function() {
53666                 var disable = operation.disabled();
53667                 return disable ?
53668                     _t('operations.copy.' + disable + '.' + _multi) :
53669                     _t('operations.copy.description' + '.' + _multi);
53670             };
53671
53672
53673             operation.annotation = function() {
53674                 return selectedIDs.length === 1 ?
53675                     _t('operations.copy.annotation.single') :
53676                     _t('operations.copy.annotation.multiple', { n: selectedIDs.length.toString() });
53677             };
53678
53679
53680             var _point;
53681             operation.point = function(val) {
53682                 _point = val;
53683                 return operation;
53684             };
53685
53686
53687             operation.id = 'copy';
53688             operation.keys = [uiCmd('⌘C')];
53689             operation.title = _t('operations.copy.title');
53690             operation.behavior = behaviorOperation(context).which(operation);
53691
53692             return operation;
53693         }
53694
53695         function operationDisconnect(context, selectedIDs) {
53696             var _vertexIDs = [];
53697             var _wayIDs = [];
53698             var _otherIDs = [];
53699             var _actions = [];
53700
53701             selectedIDs.forEach(function(id) {
53702                 var entity = context.entity(id);
53703                 if (entity.type === 'way'){
53704                     _wayIDs.push(id);
53705                 } else if (entity.geometry(context.graph()) === 'vertex') {
53706                     _vertexIDs.push(id);
53707                 } else {
53708                     _otherIDs.push(id);
53709                 }
53710             });
53711
53712             var _extent, _nodes, _coords, _descriptionID = '', _annotationID = 'features';
53713
53714             if (_vertexIDs.length > 0) {
53715                 // At the selected vertices, disconnect the selected ways, if any, else
53716                 // disconnect all connected ways
53717
53718                 _extent = utilTotalExtent(_vertexIDs, context.graph());
53719
53720                 _vertexIDs.forEach(function(vertexID) {
53721                     var action = actionDisconnect(vertexID);
53722
53723                     if (_wayIDs.length > 0) {
53724                         var waysIDsForVertex = _wayIDs.filter(function(wayID) {
53725                             var way = context.entity(wayID);
53726                             return way.nodes.indexOf(vertexID) !== -1;
53727                         });
53728                         action.limitWays(waysIDsForVertex);
53729                     }
53730                     _actions.push(action);
53731                 });
53732
53733                 _descriptionID += _actions.length === 1 ? 'single_point.' : 'multiple_points.';
53734                 if (_wayIDs.length === 1) {
53735                     _descriptionID += 'single_way.' + context.graph().geometry(_wayIDs[0]);
53736                 } else {
53737                     _descriptionID += _wayIDs.length === 0 ? 'no_ways' : 'multiple_ways';
53738                 }
53739
53740             } else if (_wayIDs.length > 0) {
53741                 // Disconnect the selected ways from each other, if they're connected,
53742                 // else disconnect them from all connected ways
53743
53744                 var ways = _wayIDs.map(function(id) {
53745                     return context.entity(id);
53746                 });
53747                 _nodes = utilGetAllNodes(_wayIDs, context.graph());
53748                 _coords = _nodes.map(function(n) { return n.loc; });
53749                 _extent = utilTotalExtent(ways, context.graph());
53750
53751                 // actions for connected nodes shared by at least two selected ways
53752                 var sharedActions = [];
53753                 // actions for connected nodes
53754                 var unsharedActions = [];
53755
53756                 _nodes.forEach(function(node) {
53757                     var action = actionDisconnect(node.id).limitWays(_wayIDs);
53758                     if (action.disabled(context.graph()) !== 'not_connected') {
53759
53760                         var count = 0;
53761                         for (var i in ways) {
53762                             var way = ways[i];
53763                             if (way.nodes.indexOf(node.id) !== -1) {
53764                                 count += 1;
53765                             }
53766                             if (count > 1) { break; }
53767                         }
53768
53769                         if (count > 1) {
53770                             sharedActions.push(action);
53771                         } else {
53772                             unsharedActions.push(action);
53773                         }
53774                     }
53775                 });
53776
53777                 _descriptionID += 'no_points.';
53778                 _descriptionID += _wayIDs.length === 1 ? 'single_way.' : 'multiple_ways.';
53779
53780                 if (sharedActions.length) {
53781                     // if any nodes are shared, only disconnect the selected ways from each other
53782                     _actions = sharedActions;
53783                     _descriptionID += 'conjoined';
53784                     _annotationID = 'from_each_other';
53785                 } else {
53786                     // if no nodes are shared, disconnect the selected ways from all connected ways
53787                     _actions = unsharedActions;
53788                     if (_wayIDs.length === 1) {
53789                         _descriptionID += context.graph().geometry(_wayIDs[0]);
53790                     } else {
53791                         _descriptionID += 'separate';
53792                     }
53793                 }
53794             }
53795
53796
53797             var operation = function() {
53798                 context.perform(function(graph) {
53799                     return _actions.reduce(function(graph, action) { return action(graph); }, graph);
53800                 }, operation.annotation());
53801
53802                 context.validator().validate();
53803             };
53804
53805
53806             operation.available = function() {
53807                 if (_actions.length === 0) { return false; }
53808                 if (_otherIDs.length !== 0) { return false; }
53809
53810                 if (_vertexIDs.length !== 0 && _wayIDs.length !== 0 && !_wayIDs.every(function(wayID) {
53811                     return _vertexIDs.some(function(vertexID) {
53812                         var way = context.entity(wayID);
53813                         return way.nodes.indexOf(vertexID) !== -1;
53814                     });
53815                 })) { return false; }
53816
53817                 return true;
53818             };
53819
53820
53821             operation.disabled = function() {
53822                 var reason;
53823                 for (var actionIndex in _actions) {
53824                     reason = _actions[actionIndex].disabled(context.graph());
53825                     if (reason) { return reason; }
53826                 }
53827
53828                 if (_extent && _extent.percentContainedIn(context.map().extent()) < 0.8) {
53829                     return 'too_large.' + ((_vertexIDs.length ? _vertexIDs : _wayIDs).length === 1 ? 'single' : 'multiple');
53830                 } else if (_coords && someMissing()) {
53831                     return 'not_downloaded';
53832                 } else if (selectedIDs.some(context.hasHiddenConnections)) {
53833                     return 'connected_to_hidden';
53834                 }
53835
53836                 return false;
53837
53838
53839                 function someMissing() {
53840                     if (context.inIntro()) { return false; }
53841                     var osm = context.connection();
53842                     if (osm) {
53843                         var missing = _coords.filter(function(loc) { return !osm.isDataLoaded(loc); });
53844                         if (missing.length) {
53845                             missing.forEach(function(loc) { context.loadTileAtLoc(loc); });
53846                             return true;
53847                         }
53848                     }
53849                     return false;
53850                 }
53851             };
53852
53853
53854             operation.tooltip = function() {
53855                 var disable = operation.disabled();
53856                 if (disable) {
53857                     return _t('operations.disconnect.' + disable);
53858                 }
53859                 return _t('operations.disconnect.description.' + _descriptionID);
53860             };
53861
53862
53863             operation.annotation = function() {
53864                 return _t('operations.disconnect.annotation.' + _annotationID);
53865             };
53866
53867
53868             operation.id = 'disconnect';
53869             operation.keys = [_t('operations.disconnect.key')];
53870             operation.title = _t('operations.disconnect.title');
53871             operation.behavior = behaviorOperation(context).which(operation);
53872
53873             return operation;
53874         }
53875
53876         function operationDowngrade(context, selectedIDs) {
53877             var affectedFeatureCount = 0;
53878             var downgradeType;
53879
53880             setDowngradeTypeForEntityIDs();
53881
53882             var multi = affectedFeatureCount === 1 ? 'single' : 'multiple';
53883
53884             function setDowngradeTypeForEntityIDs() {
53885                 for (var i in selectedIDs) {
53886                     var entityID = selectedIDs[i];
53887                     var type = downgradeTypeForEntityID(entityID);
53888                     if (type) {
53889                         affectedFeatureCount += 1;
53890                         if (downgradeType && type !== downgradeType) {
53891                             downgradeType = 'building_address';
53892                         } else {
53893                             downgradeType = type;
53894                         }
53895                     }
53896                 }
53897             }
53898
53899             function downgradeTypeForEntityID(entityID) {
53900                 var graph = context.graph();
53901                 var entity = graph.entity(entityID);
53902                 var preset = _mainPresetIndex.match(entity, graph);
53903
53904                 if (!preset || preset.isFallback()) { return null; }
53905
53906                 if (entity.type === 'node' &&
53907                     preset.id !== 'address' &&
53908                     Object.keys(entity.tags).some(function(key) {
53909                         return key.match(/^addr:.{1,}/);
53910                     })) {
53911
53912                     return 'address';
53913                 }
53914                 if (entity.geometry(graph) === 'area' &&
53915                     entity.tags.building &&
53916                     !preset.tags.building) {
53917
53918                     return 'building';
53919                 }
53920
53921                 return null;
53922             }
53923
53924             var buildingKeysToKeep = ['architect', 'building', 'height', 'layer', 'source', 'type', 'wheelchair'];
53925             var addressKeysToKeep = ['source'];
53926
53927             var operation = function () {
53928                 context.perform(function(graph) {
53929
53930                     for (var i in selectedIDs) {
53931                         var entityID = selectedIDs[i];
53932                         var type = downgradeTypeForEntityID(entityID);
53933                         if (!type) { continue; }
53934
53935                         var tags = Object.assign({}, graph.entity(entityID).tags);  // shallow copy
53936                         for (var key in tags) {
53937                             if (type === 'address' && addressKeysToKeep.indexOf(key) !== -1) { continue; }
53938                             if (type === 'building') {
53939                                 if (buildingKeysToKeep.indexOf(key) !== -1 ||
53940                                     key.match(/^building:.{1,}/) ||
53941                                     key.match(/^roof:.{1,}/)) { continue; }
53942                             }
53943                             // keep address tags for buildings too
53944                             if (key.match(/^addr:.{1,}/)) { continue; }
53945
53946                             delete tags[key];
53947                         }
53948                         graph = actionChangeTags(entityID, tags)(graph);
53949                     }
53950                     return graph;
53951                 }, operation.annotation());
53952
53953                 context.validator().validate();
53954
53955                 // refresh the select mode to enable the delete operation
53956                 context.enter(modeSelect(context, selectedIDs));
53957             };
53958
53959
53960             operation.available = function () {
53961                 return downgradeType;
53962             };
53963
53964
53965             operation.disabled = function () {
53966                 if (selectedIDs.some(hasWikidataTag)) {
53967                     return 'has_wikidata_tag';
53968                 }
53969                 return false;
53970
53971                 function hasWikidataTag(id) {
53972                     var entity = context.entity(id);
53973                     return entity.tags.wikidata && entity.tags.wikidata.trim().length > 0;
53974                 }
53975             };
53976
53977
53978             operation.tooltip = function () {
53979                 var disable = operation.disabled();
53980                 return disable ?
53981                     _t('operations.downgrade.' + disable + '.' + multi) :
53982                     _t('operations.downgrade.description.' + downgradeType);
53983             };
53984
53985
53986             operation.annotation = function () {
53987                 var suffix;
53988                 if (downgradeType === 'building_address') {
53989                     suffix = 'multiple';
53990                 } else {
53991                     suffix = downgradeType + '.' + multi;
53992                 }
53993                 return _t('operations.downgrade.annotation.' + suffix, { n: affectedFeatureCount});
53994             };
53995
53996
53997             operation.id = 'downgrade';
53998             operation.keys = [uiCmd('⌘⌫'), uiCmd('⌘⌦'), uiCmd('⌦')];
53999             operation.title = _t('operations.downgrade.title');
54000             operation.behavior = behaviorOperation(context).which(operation);
54001
54002
54003             return operation;
54004         }
54005
54006         function operationExtract(context, selectedIDs) {
54007
54008             var _amount = selectedIDs.length === 1 ? 'single' : 'multiple';
54009             var _geometries = utilArrayUniq(selectedIDs.map(function(entityID) {
54010                 return context.graph().hasEntity(entityID) && context.graph().geometry(entityID);
54011             }).filter(Boolean));
54012             var _geometryID = _geometries.length === 1 ? _geometries[0] : 'feature';
54013
54014             var _extent;
54015             var _actions = selectedIDs.map(function(entityID) {
54016                 var graph = context.graph();
54017                 var entity = graph.hasEntity(entityID);
54018                 if (!entity || !entity.hasInterestingTags()) { return; }
54019
54020                 if (entity.type === 'node' && graph.parentWays(entity).length === 0) { return; }
54021
54022                 if (entity.type !== 'node') {
54023                     var preset = _mainPresetIndex.match(entity, graph);
54024                     // only allow extraction from ways/relations if the preset supports points
54025                     if (preset.geometry.indexOf('point') === -1) { return; }
54026                 }
54027
54028                 _extent = _extent ? _extent.extend(entity.extent(graph)) : entity.extent(graph);
54029
54030                 return actionExtract(entityID);
54031             }).filter(Boolean);
54032
54033
54034             var operation = function () {
54035                 var combinedAction = function(graph) {
54036                     _actions.forEach(function(action) {
54037                         graph = action(graph);
54038                     });
54039                     return graph;
54040                 };
54041                 context.perform(combinedAction, operation.annotation());  // do the extract
54042
54043                 var extractedNodeIDs = _actions.map(function(action) {
54044                     return action.getExtractedNodeID();
54045                 });
54046                 context.enter(modeSelect(context, extractedNodeIDs));
54047             };
54048
54049
54050             operation.available = function () {
54051                 return _actions.length && selectedIDs.length === _actions.length;
54052             };
54053
54054
54055             operation.disabled = function () {
54056
54057                 if (_extent && _extent.percentContainedIn(context.map().extent()) < 0.8) {
54058                     return 'too_large';
54059                 } else if (selectedIDs.some(function(entityID) {
54060                     return context.graph().geometry(entityID) === 'vertex' && context.hasHiddenConnections(entityID);
54061                 })) {
54062                     return 'connected_to_hidden';
54063                 }
54064
54065                 return false;
54066             };
54067
54068
54069             operation.tooltip = function () {
54070                 var disableReason = operation.disabled();
54071                 if (disableReason) {
54072                     return _t('operations.extract.' + disableReason + '.' + _amount);
54073                 } else {
54074                     return _t('operations.extract.description.' + _geometryID + '.' + _amount);
54075                 }
54076             };
54077
54078
54079             operation.annotation = function () {
54080                 return _t('operations.extract.annotation.' + _amount, { n: selectedIDs.length });
54081             };
54082
54083
54084             operation.id = 'extract';
54085             operation.keys = [_t('operations.extract.key')];
54086             operation.title = _t('operations.extract.title');
54087             operation.behavior = behaviorOperation(context).which(operation);
54088
54089
54090             return operation;
54091         }
54092
54093         function operationMerge(context, selectedIDs) {
54094
54095             var _action = getAction();
54096
54097             function getAction() {
54098                 // prefer a non-disabled action first
54099                 var join = actionJoin(selectedIDs);
54100                 if (!join.disabled(context.graph())) { return join; }
54101
54102                 var merge = actionMerge(selectedIDs);
54103                 if (!merge.disabled(context.graph())) { return merge; }
54104
54105                 var mergePolygon = actionMergePolygon(selectedIDs);
54106                 if (!mergePolygon.disabled(context.graph())) { return mergePolygon; }
54107
54108                 var mergeNodes = actionMergeNodes(selectedIDs);
54109                 if (!mergeNodes.disabled(context.graph())) { return mergeNodes; }
54110
54111                 // otherwise prefer an action with an interesting disabled reason
54112                 if (join.disabled(context.graph()) !== 'not_eligible') { return join; }
54113                 if (merge.disabled(context.graph()) !== 'not_eligible') { return merge; }
54114                 if (mergePolygon.disabled(context.graph()) !== 'not_eligible') { return mergePolygon; }
54115
54116                 return mergeNodes;
54117             }
54118
54119             var operation = function() {
54120
54121                 if (operation.disabled()) { return; }
54122
54123                 context.perform(_action, operation.annotation());
54124
54125                 context.validator().validate();
54126
54127                 var resultIDs = selectedIDs.filter(context.hasEntity);
54128                 if (resultIDs.length > 1) {
54129                     var interestingIDs = resultIDs.filter(function(id) {
54130                         return context.entity(id).hasInterestingTags();
54131                     });
54132                     if (interestingIDs.length) { resultIDs = interestingIDs; }
54133                 }
54134                 context.enter(modeSelect(context, resultIDs));
54135             };
54136
54137             operation.available = function() {
54138                 return selectedIDs.length >= 2;
54139             };
54140
54141             operation.disabled = function() {
54142                 var actionDisabled = _action.disabled(context.graph());
54143                 if (actionDisabled) { return actionDisabled; }
54144
54145                 var osm = context.connection();
54146                 if (osm &&
54147                     _action.resultingWayNodesLength &&
54148                     _action.resultingWayNodesLength(context.graph()) > osm.maxWayNodes()) {
54149                     return 'too_many_vertices';
54150                 }
54151
54152                 return false;
54153             };
54154
54155             operation.tooltip = function() {
54156                 var disabled = operation.disabled();
54157                 if (disabled) {
54158                     if (disabled === 'restriction') {
54159                         return _t('operations.merge.restriction',
54160                             { relation: _mainPresetIndex.item('type/restriction').name() });
54161                     }
54162                     return _t('operations.merge.' + disabled);
54163                 }
54164                 return _t('operations.merge.description');
54165             };
54166
54167             operation.annotation = function() {
54168                 return _t('operations.merge.annotation', { n: selectedIDs.length });
54169             };
54170
54171             operation.id = 'merge';
54172             operation.keys = [_t('operations.merge.key')];
54173             operation.title = _t('operations.merge.title');
54174             operation.behavior = behaviorOperation(context).which(operation);
54175
54176             return operation;
54177         }
54178
54179         // see also `behaviorPaste`
54180         function operationPaste(context) {
54181
54182             var _pastePoint;
54183
54184             var operation = function() {
54185
54186                 if (!_pastePoint) { return; }
54187
54188                 var oldIDs = context.copyIDs();
54189                 if (!oldIDs.length) { return; }
54190
54191                 var projection = context.projection;
54192                 var extent = geoExtent();
54193                 var oldGraph = context.copyGraph();
54194                 var newIDs = [];
54195
54196                 var action = actionCopyEntities(oldIDs, oldGraph);
54197                 context.perform(action);
54198
54199                 var copies = action.copies();
54200                 var originals = new Set();
54201                 Object.values(copies).forEach(function(entity) { originals.add(entity.id); });
54202
54203                 for (var id in copies) {
54204                     var oldEntity = oldGraph.entity(id);
54205                     var newEntity = copies[id];
54206
54207                     extent._extend(oldEntity.extent(oldGraph));
54208
54209                     // Exclude child nodes from newIDs if their parent way was also copied.
54210                     var parents = context.graph().parentWays(newEntity);
54211                     var parentCopied = parents.some(function(parent) {
54212                         return originals.has(parent.id);
54213                     });
54214
54215                     if (!parentCopied) {
54216                         newIDs.push(newEntity.id);
54217                     }
54218                 }
54219
54220                 // Use the location of the copy operation to offset the paste location,
54221                 // or else use the center of the pasted extent
54222                 var copyPoint = (context.copyLonLat() && projection(context.copyLonLat())) ||
54223                     projection(extent.center());
54224                 var delta = geoVecSubtract(_pastePoint, copyPoint);
54225
54226                 // Move the pasted objects to be anchored at the paste location
54227                 context.replace(actionMove(newIDs, delta, projection), operation.annotation());
54228                 context.enter(modeSelect(context, newIDs));
54229             };
54230
54231             operation.point = function(val) {
54232                 _pastePoint = val;
54233                 return operation;
54234             };
54235
54236             operation.available = function() {
54237                 return context.mode().id === 'browse';
54238             };
54239
54240             operation.disabled = function() {
54241                 return !context.copyIDs().length;
54242             };
54243
54244             operation.tooltip = function() {
54245                 var oldGraph = context.copyGraph();
54246                 var ids = context.copyIDs();
54247                 if (!ids.length) {
54248                     return _t('operations.paste.nothing_copied');
54249                 }
54250                 return ids.length === 1 ?
54251                     _t('operations.paste.description.single', { feature: utilDisplayLabel(oldGraph.entity(ids[0]), oldGraph) }) :
54252                     _t('operations.paste.description.multiple', { n: ids.length.toString() });
54253             };
54254
54255             operation.annotation = function() {
54256                 var ids = context.copyIDs();
54257                 return ids.length === 1 ?
54258                     _t('operations.paste.annotation.single') :
54259                     _t('operations.paste.annotation.multiple', { n: ids.length.toString() });
54260             };
54261
54262             operation.id = 'paste';
54263             operation.keys = [uiCmd('⌘V')];
54264             operation.title = _t('operations.paste.title');
54265
54266             return operation;
54267         }
54268
54269         function operationReverse(context, selectedIDs) {
54270
54271             var operation = function() {
54272                 context.perform(function combinedReverseAction(graph) {
54273                     actions().forEach(function(action) {
54274                         graph = action(graph);
54275                     });
54276                     return graph;
54277                 }, operation.annotation());
54278                 context.validator().validate();
54279             };
54280
54281             function actions(situation) {
54282                 return selectedIDs.map(function(entityID) {
54283                     var entity = context.hasEntity(entityID);
54284                     if (!entity) { return; }
54285
54286                     if (situation === 'toolbar') {
54287                         if (entity.type === 'way' &&
54288                             (!entity.isOneWay() && !entity.isSided())) { return; }
54289                     }
54290
54291                     var geometry = entity.geometry(context.graph());
54292                     if (entity.type !== 'node' && geometry !== 'line') { return; }
54293
54294                     var action = actionReverse(entityID);
54295                     if (action.disabled(context.graph())) { return; }
54296
54297                     return action;
54298                 }).filter(Boolean);
54299             }
54300
54301             function reverseTypeID() {
54302                 var acts = actions();
54303                 var nodeActionCount = acts.filter(function(act) {
54304                     var entity = context.hasEntity(act.entityID());
54305                     return entity && entity.type === 'node';
54306                 }).length;
54307                 var typeID = nodeActionCount === 0 ? 'line' : (nodeActionCount === acts.length ? 'point' : 'features');
54308                 if (typeID !== 'features' && acts.length > 1) { typeID += 's'; }
54309                 return typeID;
54310             }
54311
54312
54313             operation.available = function(situation) {
54314                 return actions(situation).length > 0;
54315             };
54316
54317
54318             operation.disabled = function() {
54319                 return false;
54320             };
54321
54322
54323             operation.tooltip = function() {
54324                 return _t('operations.reverse.description.' + reverseTypeID());
54325             };
54326
54327
54328             operation.annotation = function() {
54329                 return _t('operations.reverse.annotation.' + reverseTypeID());
54330             };
54331
54332
54333             operation.id = 'reverse';
54334             operation.keys = [_t('operations.reverse.key')];
54335             operation.title = _t('operations.reverse.title');
54336             operation.behavior = behaviorOperation(context).which(operation);
54337
54338             return operation;
54339         }
54340
54341         function operationSplit(context, selectedIDs) {
54342             var vertices = selectedIDs
54343                 .filter(function(id) { return context.graph().geometry(id) === 'vertex'; });
54344             var entityID = vertices[0];
54345             var action = actionSplit(entityID);
54346             var ways = [];
54347
54348             if (vertices.length === 1) {
54349                 if (entityID && selectedIDs.length > 1) {
54350                     var ids = selectedIDs.filter(function(id) { return id !== entityID; });
54351                     action.limitWays(ids);
54352                 }
54353                 ways = action.ways(context.graph());
54354             }
54355
54356
54357             var operation = function() {
54358                 var difference = context.perform(action, operation.annotation());
54359                 context.enter(modeSelect(context, difference.extantIDs()));
54360             };
54361
54362
54363             operation.available = function() {
54364                 return vertices.length === 1;
54365             };
54366
54367
54368             operation.disabled = function() {
54369                 var reason = action.disabled(context.graph());
54370                 if (reason) {
54371                     return reason;
54372                 } else if (selectedIDs.some(context.hasHiddenConnections)) {
54373                     return 'connected_to_hidden';
54374                 }
54375
54376                 return false;
54377             };
54378
54379
54380             operation.tooltip = function() {
54381                 var disable = operation.disabled();
54382                 if (disable) {
54383                     return _t('operations.split.' + disable);
54384                 } else if (ways.length === 1) {
54385                     return _t('operations.split.description.' + context.graph().geometry(ways[0].id));
54386                 } else {
54387                     return _t('operations.split.description.multiple');
54388                 }
54389             };
54390
54391
54392             operation.annotation = function() {
54393                 return ways.length === 1 ?
54394                     _t('operations.split.annotation.' + context.graph().geometry(ways[0].id)) :
54395                     _t('operations.split.annotation.multiple', { n: ways.length });
54396             };
54397
54398
54399             operation.id = 'split';
54400             operation.keys = [_t('operations.split.key')];
54401             operation.title = _t('operations.split.title');
54402             operation.behavior = behaviorOperation(context).which(operation);
54403
54404             return operation;
54405         }
54406
54407         function operationStraighten(context, selectedIDs) {
54408             var _wayIDs = selectedIDs.filter(function(id) { return id.charAt(0) === 'w'; });
54409             var _nodeIDs = selectedIDs.filter(function(id) { return id.charAt(0) === 'n'; });
54410             var _amount = ((_wayIDs.length ? _wayIDs : _nodeIDs).length === 1 ? 'single' : 'multiple');
54411
54412             var _nodes = utilGetAllNodes(selectedIDs, context.graph());
54413             var _coords = _nodes.map(function(n) { return n.loc; });
54414             var _extent = utilTotalExtent(selectedIDs, context.graph());
54415             var _action = chooseAction();
54416             var _geometry;
54417
54418
54419             function chooseAction() {
54420                 // straighten selected nodes
54421                 if (_wayIDs.length === 0 && _nodeIDs.length > 2) {
54422                     _geometry = 'points';
54423                     return actionStraightenNodes(_nodeIDs, context.projection);
54424
54425                 // straighten selected ways (possibly between range of 2 selected nodes)
54426                 } else if (_wayIDs.length > 0 && (_nodeIDs.length === 0 || _nodeIDs.length === 2)) {
54427                     var startNodeIDs = [];
54428                     var endNodeIDs = [];
54429
54430                     for (var i = 0; i < selectedIDs.length; i++) {
54431                         var entity = context.entity(selectedIDs[i]);
54432                         if (entity.type === 'node') {
54433                             continue;
54434                         } else if (entity.type !== 'way' || entity.isClosed()) {
54435                             return null;  // exit early, can't straighten these
54436                         }
54437
54438                         startNodeIDs.push(entity.first());
54439                         endNodeIDs.push(entity.last());
54440                     }
54441
54442                     // Remove duplicate end/startNodeIDs (duplicate nodes cannot be at the line end)
54443                     startNodeIDs = startNodeIDs.filter(function(n) {
54444                         return startNodeIDs.indexOf(n) === startNodeIDs.lastIndexOf(n);
54445                     });
54446                     endNodeIDs = endNodeIDs.filter(function(n) {
54447                         return endNodeIDs.indexOf(n) === endNodeIDs.lastIndexOf(n);
54448                     });
54449
54450                     // Ensure all ways are connected (i.e. only 2 unique endpoints/startpoints)
54451                     if (utilArrayDifference(startNodeIDs, endNodeIDs).length +
54452                         utilArrayDifference(endNodeIDs, startNodeIDs).length !== 2) { return null; }
54453
54454                     // Ensure path contains at least 3 unique nodes
54455                     var wayNodeIDs = utilGetAllNodes(_wayIDs, context.graph())
54456                         .map(function(node) { return node.id; });
54457                     if (wayNodeIDs.length <= 2) { return null; }
54458
54459                     // If range of 2 selected nodes is supplied, ensure nodes lie on the selected path
54460                     if (_nodeIDs.length === 2 && (
54461                         wayNodeIDs.indexOf(_nodeIDs[0]) === -1 || wayNodeIDs.indexOf(_nodeIDs[1]) === -1
54462                     )) { return null; }
54463
54464                     if (_nodeIDs.length) {
54465                         // If we're only straightenting between two points, we only need that extent visible
54466                         _extent = utilTotalExtent(_nodeIDs, context.graph());
54467                     }
54468
54469                     _geometry = _wayIDs.length === 1 ? 'line' : 'lines';
54470                     return actionStraightenWay(selectedIDs, context.projection);
54471                 }
54472
54473                 return null;
54474             }
54475
54476
54477             function operation() {
54478                 if (!_action) { return; }
54479
54480                 context.perform(_action, operation.annotation());
54481
54482                 window.setTimeout(function() {
54483                     context.validator().validate();
54484                 }, 300);  // after any transition
54485             }
54486
54487
54488             operation.available = function() {
54489                 return Boolean(_action);
54490             };
54491
54492
54493             operation.disabled = function() {
54494                 var reason = _action.disabled(context.graph());
54495                 if (reason) {
54496                     return reason;
54497                 } else if (_extent.percentContainedIn(context.map().extent()) < 0.8) {
54498                     return 'too_large';
54499                 } else if (someMissing()) {
54500                     return 'not_downloaded';
54501                 } else if (selectedIDs.some(context.hasHiddenConnections)) {
54502                     return 'connected_to_hidden';
54503                 }
54504
54505                 return false;
54506
54507
54508                 function someMissing() {
54509                     if (context.inIntro()) { return false; }
54510                     var osm = context.connection();
54511                     if (osm) {
54512                         var missing = _coords.filter(function(loc) { return !osm.isDataLoaded(loc); });
54513                         if (missing.length) {
54514                             missing.forEach(function(loc) { context.loadTileAtLoc(loc); });
54515                             return true;
54516                         }
54517                     }
54518                     return false;
54519                 }
54520             };
54521
54522
54523             operation.tooltip = function() {
54524                 var disable = operation.disabled();
54525                 return disable ?
54526                     _t('operations.straighten.' + disable + '.' + _amount) :
54527                     _t('operations.straighten.description.' + _geometry);
54528             };
54529
54530
54531             operation.annotation = function() {
54532                 return _t('operations.straighten.annotation.' + _geometry);
54533             };
54534
54535
54536             operation.id = 'straighten';
54537             operation.keys = [_t('operations.straighten.key')];
54538             operation.title = _t('operations.straighten.title');
54539             operation.behavior = behaviorOperation(context).which(operation);
54540
54541             return operation;
54542         }
54543
54544         var Operations = /*#__PURE__*/Object.freeze({
54545                 __proto__: null,
54546                 operationCircularize: operationCircularize,
54547                 operationContinue: operationContinue,
54548                 operationCopy: operationCopy,
54549                 operationDelete: operationDelete,
54550                 operationDisconnect: operationDisconnect,
54551                 operationDowngrade: operationDowngrade,
54552                 operationExtract: operationExtract,
54553                 operationMerge: operationMerge,
54554                 operationMove: operationMove,
54555                 operationOrthogonalize: operationOrthogonalize,
54556                 operationPaste: operationPaste,
54557                 operationReflectShort: operationReflectShort,
54558                 operationReflectLong: operationReflectLong,
54559                 operationReverse: operationReverse,
54560                 operationRotate: operationRotate,
54561                 operationSplit: operationSplit,
54562                 operationStraighten: operationStraighten
54563         });
54564
54565         var _relatedParent;
54566
54567
54568         function modeSelect(context, selectedIDs) {
54569             var mode = {
54570                 id: 'select',
54571                 button: 'browse'
54572             };
54573
54574             var keybinding = utilKeybinding('select');
54575
54576             var _breatheBehavior = behaviorBreathe();
54577             var _modeDragNode = modeDragNode(context);
54578             var _selectBehavior;
54579             var _behaviors = [];
54580
54581             var _operations = [];
54582             var _newFeature = false;
54583             var _follow = false;
54584
54585
54586             function singular() {
54587                 if (selectedIDs && selectedIDs.length === 1) {
54588                     return context.hasEntity(selectedIDs[0]);
54589                 }
54590             }
54591
54592             function selectedEntities() {
54593                 return selectedIDs.map(function(id) {
54594                     return context.hasEntity(id);
54595                 }).filter(Boolean);
54596             }
54597
54598
54599             function checkSelectedIDs() {
54600                 var ids = [];
54601                 if (Array.isArray(selectedIDs)) {
54602                     ids = selectedIDs.filter(function(id) {
54603                         return context.hasEntity(id);
54604                     });
54605                 }
54606
54607                 if (!ids.length) {
54608                     context.enter(modeBrowse(context));
54609                     return false;
54610                 } else if ((selectedIDs.length > 1 && ids.length === 1) ||
54611                     (selectedIDs.length === 1 && ids.length > 1)) {
54612                     // switch between single- and multi-select UI
54613                     context.enter(modeSelect(context, ids));
54614                     return false;
54615                 }
54616
54617                 selectedIDs = ids;
54618                 return true;
54619             }
54620
54621
54622             // find the common parent ways for nextVertex, previousVertex
54623             function commonParents() {
54624                 var graph = context.graph();
54625                 var commonParents = [];
54626
54627                 for (var i = 0; i < selectedIDs.length; i++) {
54628                     var entity = context.hasEntity(selectedIDs[i]);
54629                     if (!entity || entity.geometry(graph) !== 'vertex') {
54630                         return [];  // selection includes some not vertexes
54631                     }
54632
54633                     var currParents = graph.parentWays(entity).map(function(w) { return w.id; });
54634                     if (!commonParents.length) {
54635                         commonParents = currParents;
54636                         continue;
54637                     }
54638
54639                     commonParents = utilArrayIntersection(commonParents, currParents);
54640                     if (!commonParents.length) {
54641                         return [];
54642                     }
54643                 }
54644
54645                 return commonParents;
54646             }
54647
54648
54649             function singularParent() {
54650                 var parents = commonParents();
54651                 if (!parents || parents.length === 0) {
54652                     _relatedParent = null;
54653                     return null;
54654                 }
54655
54656                 // relatedParent is used when we visit a vertex with multiple
54657                 // parents, and we want to remember which parent line we started on.
54658
54659                 if (parents.length === 1) {
54660                     _relatedParent = parents[0];  // remember this parent for later
54661                     return _relatedParent;
54662                 }
54663
54664                 if (parents.indexOf(_relatedParent) !== -1) {
54665                     return _relatedParent;   // prefer the previously seen parent
54666                 }
54667
54668                 return parents[0];
54669             }
54670
54671
54672             mode.selectedIDs = function(val) {
54673                 if (!arguments.length) { return selectedIDs; }
54674                 selectedIDs = val;
54675                 return mode;
54676             };
54677
54678
54679             mode.zoomToSelected = function() {
54680                 context.map().zoomToEase(selectedEntities());
54681             };
54682
54683
54684             mode.newFeature = function(val) {
54685                 if (!arguments.length) { return _newFeature; }
54686                 _newFeature = val;
54687                 return mode;
54688             };
54689
54690
54691             mode.selectBehavior = function(val) {
54692                 if (!arguments.length) { return _selectBehavior; }
54693                 _selectBehavior = val;
54694                 return mode;
54695             };
54696
54697
54698             mode.follow = function(val) {
54699                 if (!arguments.length) { return _follow; }
54700                 _follow = val;
54701                 return mode;
54702             };
54703
54704             function loadOperations() {
54705
54706                 _operations.forEach(function(operation) {
54707                     if (operation.behavior) {
54708                         context.uninstall(operation.behavior);
54709                     }
54710                 });
54711
54712                 _operations = Object.values(Operations)
54713                     .map(function(o) { return o(context, selectedIDs); })
54714                     .filter(function(o) { return o.available() && o.id !== 'delete' && o.id !== 'downgrade' && o.id !== 'copy'; });
54715
54716                 var copyOperation = operationCopy(context, selectedIDs);
54717                 if (copyOperation.available()) {
54718                     // group copy operation with delete/downgrade
54719                     _operations.push(copyOperation);
54720                 }
54721
54722                 var downgradeOperation = operationDowngrade(context, selectedIDs);
54723                 // don't allow delete if downgrade is available
54724                 var lastOperation = !context.inIntro() && downgradeOperation.available() ? downgradeOperation : operationDelete(context, selectedIDs);
54725
54726                 _operations.push(lastOperation);
54727
54728                 _operations.forEach(function(operation) {
54729                     if (operation.behavior) {
54730                         context.install(operation.behavior);
54731                     }
54732                 });
54733
54734                 // remove any displayed menu
54735                 context.ui().closeEditMenu();
54736             }
54737
54738             mode.operations = function() {
54739                 return _operations;
54740             };
54741
54742
54743             mode.enter = function() {
54744                 if (!checkSelectedIDs()) { return; }
54745
54746                 context.features().forceVisible(selectedIDs);
54747
54748                 _modeDragNode.restoreSelectedIDs(selectedIDs);
54749
54750                 loadOperations();
54751
54752                 if (!_behaviors.length) {
54753                     if (!_selectBehavior) { _selectBehavior = behaviorSelect(context); }
54754
54755                     _behaviors = [
54756                         behaviorPaste(context),
54757                         _breatheBehavior,
54758                         behaviorHover(context).on('hover', context.ui().sidebar.hoverModeSelect),
54759                         _selectBehavior,
54760                         behaviorLasso(context),
54761                         _modeDragNode.behavior,
54762                         modeDragNote(context).behavior
54763                     ];
54764                 }
54765                 _behaviors.forEach(context.install);
54766
54767                 keybinding
54768                     .on(_t('inspector.zoom_to.key'), mode.zoomToSelected)
54769                     .on(['[', 'pgup'], previousVertex)
54770                     .on([']', 'pgdown'], nextVertex)
54771                     .on(['{', uiCmd('⌘['), 'home'], firstVertex)
54772                     .on(['}', uiCmd('⌘]'), 'end'], lastVertex)
54773                     .on(uiCmd('⇧←'), nudgeSelection([-10, 0]))
54774                     .on(uiCmd('⇧↑'), nudgeSelection([0, -10]))
54775                     .on(uiCmd('⇧→'), nudgeSelection([10, 0]))
54776                     .on(uiCmd('⇧↓'), nudgeSelection([0, 10]))
54777                     .on(uiCmd('⇧⌘←'), nudgeSelection([-100, 0]))
54778                     .on(uiCmd('⇧⌘↑'), nudgeSelection([0, -100]))
54779                     .on(uiCmd('⇧⌘→'), nudgeSelection([100, 0]))
54780                     .on(uiCmd('⇧⌘↓'), nudgeSelection([0, 100]))
54781                     .on(['\\', 'pause'], nextParent)
54782                     .on('⎋', esc, true);
54783
54784                 select(document)
54785                     .call(keybinding);
54786
54787                 context.ui().sidebar
54788                     .select(selectedIDs, _newFeature);
54789
54790                 context.history()
54791                     .on('change.select', function() {
54792                         loadOperations();
54793                         // reselect after change in case relation members were removed or added
54794                         selectElements();
54795                     })
54796                     .on('undone.select', checkSelectedIDs)
54797                     .on('redone.select', checkSelectedIDs);
54798
54799                 context.map()
54800                     .on('drawn.select', selectElements)
54801                     .on('crossEditableZoom.select', function() {
54802                         selectElements();
54803                         _breatheBehavior.restartIfNeeded(context.surface());
54804                     });
54805
54806                 context.map().doubleUpHandler()
54807                     .on('doubleUp.modeSelect', didDoubleUp);
54808
54809
54810                 selectElements();
54811
54812                 if (_follow) {
54813                     var extent = geoExtent();
54814                     var graph = context.graph();
54815                     selectedIDs.forEach(function(id) {
54816                         var entity = context.entity(id);
54817                         extent._extend(entity.extent(graph));
54818                     });
54819
54820                     var loc = extent.center();
54821                     context.map().centerEase(loc);
54822                     // we could enter the mode multiple times, so reset follow for next time
54823                     _follow = false;
54824                 }
54825
54826
54827                 function nudgeSelection(delta) {
54828                     return function() {
54829                         // prevent nudging during low zoom selection
54830                         if (!context.map().withinEditableZoom()) { return; }
54831
54832                         var moveOp = operationMove(context, selectedIDs);
54833                         if (moveOp.disabled()) {
54834                             context.ui().flash
54835                                 .duration(4000)
54836                                 .iconName('#iD-operation-' + moveOp.id)
54837                                 .iconClass('operation disabled')
54838                                 .text(moveOp.tooltip)();
54839                         } else {
54840                             context.perform(actionMove(selectedIDs, delta, context.projection), moveOp.annotation());
54841                         }
54842                     };
54843                 }
54844
54845
54846                 function didDoubleUp(loc) {
54847                     if (!context.map().withinEditableZoom()) { return; }
54848
54849                     var target = select(event.target);
54850
54851                     var datum = target.datum();
54852                     var entity = datum && datum.properties && datum.properties.entity;
54853                     if (!entity) { return; }
54854
54855                     if (entity instanceof osmWay && target.classed('target')) {
54856                         var choice = geoChooseEdge(context.graph().childNodes(entity), loc, context.projection);
54857                         var prev = entity.nodes[choice.index - 1];
54858                         var next = entity.nodes[choice.index];
54859
54860                         context.perform(
54861                             actionAddMidpoint({ loc: choice.loc, edge: [prev, next] }, osmNode()),
54862                             _t('operations.add.annotation.vertex')
54863                         );
54864
54865                     } else if (entity.type === 'midpoint') {
54866                         context.perform(
54867                             actionAddMidpoint({ loc: entity.loc, edge: entity.edge }, osmNode()),
54868                             _t('operations.add.annotation.vertex'));
54869                     }
54870                 }
54871
54872
54873                 function selectElements() {
54874                     if (!checkSelectedIDs()) { return; }
54875
54876                     var surface = context.surface();
54877
54878                     surface.selectAll('.selected-member')
54879                         .classed('selected-member', false);
54880
54881                     surface.selectAll('.selected')
54882                         .classed('selected', false);
54883
54884                     surface.selectAll('.related')
54885                         .classed('related', false);
54886
54887                     singularParent();
54888                     if (_relatedParent) {
54889                         surface.selectAll(utilEntitySelector([_relatedParent]))
54890                             .classed('related', true);
54891                     }
54892
54893                     if (context.map().withinEditableZoom()) {
54894                         // Apply selection styling if not in wide selection
54895
54896                         surface
54897                             .selectAll(utilDeepMemberSelector(selectedIDs, context.graph(), true /* skipMultipolgonMembers */))
54898                             .classed('selected-member', true);
54899                         surface
54900                             .selectAll(utilEntityOrDeepMemberSelector(selectedIDs, context.graph()))
54901                             .classed('selected', true);
54902                     }
54903
54904                 }
54905
54906
54907                 function esc() {
54908                     if (context.container().select('.combobox').size()) { return; }
54909                     context.enter(modeBrowse(context));
54910                 }
54911
54912
54913                 function firstVertex() {
54914                     event.preventDefault();
54915                     var entity = singular();
54916                     var parent = singularParent();
54917                     var way;
54918
54919                     if (entity && entity.type === 'way') {
54920                         way = entity;
54921                     } else if (parent) {
54922                         way = context.entity(parent);
54923                     }
54924
54925                     if (way) {
54926                         context.enter(
54927                             modeSelect(context, [way.first()]).follow(true)
54928                         );
54929                     }
54930                 }
54931
54932
54933                 function lastVertex() {
54934                     event.preventDefault();
54935                     var entity = singular();
54936                     var parent = singularParent();
54937                     var way;
54938
54939                     if (entity && entity.type === 'way') {
54940                         way = entity;
54941                     } else if (parent) {
54942                         way = context.entity(parent);
54943                     }
54944
54945                     if (way) {
54946                         context.enter(
54947                             modeSelect(context, [way.last()]).follow(true)
54948                         );
54949                     }
54950                 }
54951
54952
54953                 function previousVertex() {
54954                     event.preventDefault();
54955                     var parent = singularParent();
54956                     if (!parent) { return; }
54957
54958                     var way = context.entity(parent);
54959                     var length = way.nodes.length;
54960                     var curr = way.nodes.indexOf(selectedIDs[0]);
54961                     var index = -1;
54962
54963                     if (curr > 0) {
54964                         index = curr - 1;
54965                     } else if (way.isClosed()) {
54966                         index = length - 2;
54967                     }
54968
54969                     if (index !== -1) {
54970                         context.enter(
54971                             modeSelect(context, [way.nodes[index]]).follow(true)
54972                         );
54973                     }
54974                 }
54975
54976
54977                 function nextVertex() {
54978                     event.preventDefault();
54979                     var parent = singularParent();
54980                     if (!parent) { return; }
54981
54982                     var way = context.entity(parent);
54983                     var length = way.nodes.length;
54984                     var curr = way.nodes.indexOf(selectedIDs[0]);
54985                     var index = -1;
54986
54987                     if (curr < length - 1) {
54988                         index = curr + 1;
54989                     } else if (way.isClosed()) {
54990                         index = 0;
54991                     }
54992
54993                     if (index !== -1) {
54994                         context.enter(
54995                             modeSelect(context, [way.nodes[index]]).follow(true)
54996                         );
54997                     }
54998                 }
54999
55000
55001                 function nextParent() {
55002                     event.preventDefault();
55003                     var parents = commonParents();
55004                     if (!parents || parents.length < 2) { return; }
55005
55006                     var index = parents.indexOf(_relatedParent);
55007                     if (index < 0 || index > parents.length - 2) {
55008                         _relatedParent = parents[0];
55009                     } else {
55010                         _relatedParent = parents[index + 1];
55011                     }
55012
55013                     var surface = context.surface();
55014                     surface.selectAll('.related')
55015                         .classed('related', false);
55016
55017                     if (_relatedParent) {
55018                         surface.selectAll(utilEntitySelector([_relatedParent]))
55019                             .classed('related', true);
55020                     }
55021                 }
55022             };
55023
55024
55025             mode.exit = function() {
55026
55027                 _newFeature = false;
55028
55029                 _operations.forEach(function(operation) {
55030                     if (operation.behavior) {
55031                         context.uninstall(operation.behavior);
55032                     }
55033                 });
55034                 _operations = [];
55035
55036                 _behaviors.forEach(context.uninstall);
55037
55038                 select(document)
55039                     .call(keybinding.unbind);
55040
55041                 context.ui().closeEditMenu();
55042
55043                 context.history()
55044                     .on('change.select', null)
55045                     .on('undone.select', null)
55046                     .on('redone.select', null);
55047
55048                 var surface = context.surface();
55049
55050                 surface
55051                     .selectAll('.selected-member')
55052                     .classed('selected-member', false);
55053
55054                 surface
55055                     .selectAll('.selected')
55056                     .classed('selected', false);
55057
55058                 surface
55059                     .selectAll('.highlighted')
55060                     .classed('highlighted', false);
55061
55062                 surface
55063                     .selectAll('.related')
55064                     .classed('related', false);
55065
55066                 context.map().on('drawn.select', null);
55067                 context.ui().sidebar.hide();
55068                 context.features().forceVisible([]);
55069
55070                 var entity = singular();
55071                 if (_newFeature && entity && entity.type === 'relation' &&
55072                     // no tags
55073                     Object.keys(entity.tags).length === 0 &&
55074                     // no parent relations
55075                     context.graph().parentRelations(entity).length === 0 &&
55076                     // no members or one member with no role
55077                     (entity.members.length === 0 || (entity.members.length === 1 && !entity.members[0].role))
55078                 ) {
55079                     // the user added this relation but didn't edit it at all, so just delete it
55080                     var deleteAction = actionDeleteRelation(entity.id, true /* don't delete untagged members */);
55081                     context.perform(deleteAction, _t('operations.delete.annotation.relation'));
55082                 }
55083             };
55084
55085
55086             return mode;
55087         }
55088
55089         function uiLasso(context) {
55090             var group, polygon;
55091
55092             lasso.coordinates = [];
55093
55094             function lasso(selection) {
55095                 context.container()
55096                     .classed('lasso', true);
55097
55098                 group = selection
55099                     .append('g')
55100                     .attr('class', 'lasso hide');
55101
55102                 polygon = group
55103                     .append('path')
55104                     .attr('class', 'lasso-path');
55105
55106                 group
55107                     .call(uiToggle(true));
55108             }
55109
55110
55111             function draw() {
55112                 if (polygon) {
55113                     polygon.data([lasso.coordinates])
55114                         .attr('d', function(d) { return 'M' + d.join(' L') + ' Z'; });
55115                 }
55116             }
55117
55118
55119             lasso.extent = function () {
55120                 return lasso.coordinates.reduce(function(extent, point) {
55121                     return extent.extend(geoExtent(point));
55122                 }, geoExtent());
55123             };
55124
55125
55126             lasso.p = function(_) {
55127                 if (!arguments.length) { return lasso; }
55128                 lasso.coordinates.push(_);
55129                 draw();
55130                 return lasso;
55131             };
55132
55133
55134             lasso.close = function() {
55135                 if (group) {
55136                     group.call(uiToggle(false, function() {
55137                         select(this).remove();
55138                     }));
55139                 }
55140                 context.container().classed('lasso', false);
55141             };
55142
55143
55144             return lasso;
55145         }
55146
55147         function behaviorLasso(context) {
55148
55149             // use pointer events on supported platforms; fallback to mouse events
55150             var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
55151
55152             var behavior = function(selection) {
55153                 var lasso;
55154
55155
55156                 function pointerdown() {
55157                     var button = 0;  // left
55158                     if (event.button === button && event.shiftKey === true) {
55159                         lasso = null;
55160
55161                         select(window)
55162                             .on(_pointerPrefix + 'move.lasso', pointermove)
55163                             .on(_pointerPrefix + 'up.lasso', pointerup);
55164
55165                         event.stopPropagation();
55166                     }
55167                 }
55168
55169
55170                 function pointermove() {
55171                     if (!lasso) {
55172                         lasso = uiLasso(context);
55173                         context.surface().call(lasso);
55174                     }
55175
55176                     lasso.p(context.map().mouse());
55177                 }
55178
55179
55180                 function normalize(a, b) {
55181                     return [
55182                         [Math.min(a[0], b[0]), Math.min(a[1], b[1])],
55183                         [Math.max(a[0], b[0]), Math.max(a[1], b[1])]
55184                     ];
55185                 }
55186
55187
55188                 function lassoed() {
55189                     if (!lasso) { return []; }
55190
55191                     var graph = context.graph();
55192                     var limitToNodes;
55193
55194                     if (context.map().editableDataEnabled(true /* skipZoomCheck */) && context.map().isInWideSelection()) {
55195                         // only select from the visible nodes
55196                         limitToNodes = new Set(utilGetAllNodes(context.selectedIDs(), graph));
55197                     } else if (!context.map().editableDataEnabled()) {
55198                         return [];
55199                     }
55200
55201                     var bounds = lasso.extent().map(context.projection.invert);
55202                     var extent = geoExtent(normalize(bounds[0], bounds[1]));
55203
55204                     var intersects = context.history().intersects(extent).filter(function(entity) {
55205                         return entity.type === 'node' &&
55206                             (!limitToNodes || limitToNodes.has(entity)) &&
55207                             geoPointInPolygon(context.projection(entity.loc), lasso.coordinates) &&
55208                             !context.features().isHidden(entity, graph, entity.geometry(graph));
55209                     });
55210
55211                     // sort the lassoed nodes as best we can
55212                     intersects.sort(function(node1, node2) {
55213                         var parents1 = graph.parentWays(node1);
55214                         var parents2 = graph.parentWays(node2);
55215                         if (parents1.length && parents2.length) {
55216                             // both nodes are vertices
55217
55218                             var sharedParents = utilArrayIntersection(parents1, parents2);
55219                             if (sharedParents.length) {
55220                                 var sharedParentNodes = sharedParents[0].nodes;
55221                                 // vertices are members of the same way; sort them in their listed order
55222                                 return sharedParentNodes.indexOf(node1.id) -
55223                                     sharedParentNodes.indexOf(node2.id);
55224                             } else {
55225                                 // vertices do not share a way; group them by their respective parent ways
55226                                 return parseFloat(parents1[0].id.slice(1)) -
55227                                     parseFloat(parents2[0].id.slice(1));
55228                             }
55229
55230                         } else if (parents1.length || parents2.length) {
55231                             // only one node is a vertex; sort standalone points before vertices
55232                             return parents1.length - parents2.length;
55233                         }
55234                         // both nodes are standalone points; sort left to right
55235                         return node1.loc[0] - node2.loc[0];
55236                     });
55237
55238                     return intersects.map(function(entity) { return entity.id; });
55239                 }
55240
55241
55242                 function pointerup() {
55243                     select(window)
55244                         .on(_pointerPrefix + 'move.lasso', null)
55245                         .on(_pointerPrefix + 'up.lasso', null);
55246
55247                     if (!lasso) { return; }
55248
55249                     var ids = lassoed();
55250                     lasso.close();
55251
55252                     if (ids.length) {
55253                         context.enter(modeSelect(context, ids));
55254                     }
55255                 }
55256
55257                 selection
55258                     .on(_pointerPrefix + 'down.lasso', pointerdown);
55259             };
55260
55261
55262             behavior.off = function(selection) {
55263                 selection.on(_pointerPrefix + 'down.lasso', null);
55264             };
55265
55266
55267             return behavior;
55268         }
55269
55270         function modeBrowse(context) {
55271             var mode = {
55272                 button: 'browse',
55273                 id: 'browse',
55274                 title: _t('modes.browse.title'),
55275                 description: _t('modes.browse.description')
55276             };
55277             var sidebar;
55278
55279             var _selectBehavior;
55280             var _behaviors = [];
55281
55282
55283             mode.selectBehavior = function(val) {
55284                 if (!arguments.length) { return _selectBehavior; }
55285                 _selectBehavior = val;
55286                 return mode;
55287             };
55288
55289
55290             mode.enter = function() {
55291                 if (!_behaviors.length) {
55292                     if (!_selectBehavior) { _selectBehavior = behaviorSelect(context); }
55293                     _behaviors = [
55294                         behaviorPaste(context),
55295                         behaviorHover(context).on('hover', context.ui().sidebar.hover),
55296                         _selectBehavior,
55297                         behaviorLasso(context),
55298                         modeDragNode(context).behavior,
55299                         modeDragNote(context).behavior
55300                     ];
55301                 }
55302                 _behaviors.forEach(context.install);
55303
55304                 // Get focus on the body.
55305                 if (document.activeElement && document.activeElement.blur) {
55306                     document.activeElement.blur();
55307                 }
55308
55309                 if (sidebar) {
55310                     context.ui().sidebar.show(sidebar);
55311                 } else {
55312                     context.ui().sidebar.select(null);
55313                 }
55314             };
55315
55316
55317             mode.exit = function() {
55318                 context.ui().sidebar.hover.cancel();
55319                 _behaviors.forEach(context.uninstall);
55320
55321                 if (sidebar) {
55322                     context.ui().sidebar.hide();
55323                 }
55324             };
55325
55326
55327             mode.sidebar = function(_) {
55328                 if (!arguments.length) { return sidebar; }
55329                 sidebar = _;
55330                 return mode;
55331             };
55332
55333
55334             mode.operations = function() {
55335                 return [operationPaste(context)];
55336             };
55337
55338
55339             return mode;
55340         }
55341
55342         function behaviorAddWay(context) {
55343             var dispatch$1 = dispatch('start', 'startFromWay', 'startFromNode');
55344             var draw = behaviorDraw(context);
55345
55346             function behavior(surface) {
55347                 draw.on('click', function() { dispatch$1.apply('start', this, arguments); })
55348                     .on('clickWay', function() { dispatch$1.apply('startFromWay', this, arguments); })
55349                     .on('clickNode', function() { dispatch$1.apply('startFromNode', this, arguments); })
55350                     .on('cancel', behavior.cancel)
55351                     .on('finish', behavior.cancel);
55352
55353                 context.map()
55354                     .dblclickZoomEnable(false);
55355
55356                 surface.call(draw);
55357             }
55358
55359
55360             behavior.off = function(surface) {
55361                 surface.call(draw.off);
55362             };
55363
55364
55365             behavior.cancel = function() {
55366                 window.setTimeout(function() {
55367                     context.map().dblclickZoomEnable(true);
55368                 }, 1000);
55369
55370                 context.enter(modeBrowse(context));
55371             };
55372
55373
55374             return utilRebind(behavior, dispatch$1, 'on');
55375         }
55376
55377         function behaviorHash(context) {
55378
55379             // cached window.location.hash
55380             var _cachedHash = null;
55381             // allowable latitude range
55382             var _latitudeLimit = 90 - 1e-8;
55383
55384             function computedHashParameters() {
55385                 var map = context.map();
55386                 var center = map.center();
55387                 var zoom = map.zoom();
55388                 var precision = Math.max(0, Math.ceil(Math.log(zoom) / Math.LN2));
55389                 var oldParams = utilObjectOmit(utilStringQs(window.location.hash),
55390                     ['comment', 'source', 'hashtags', 'walkthrough']
55391                 );
55392                 var newParams = {};
55393
55394                 delete oldParams.id;
55395                 var selected = context.selectedIDs().filter(function(id) {
55396                     return context.hasEntity(id);
55397                 });
55398                 if (selected.length) {
55399                     newParams.id = selected.join(',');
55400                 }
55401
55402                 newParams.map = zoom.toFixed(2) +
55403                     '/' + center[1].toFixed(precision) +
55404                     '/' + center[0].toFixed(precision);
55405
55406                 return Object.assign(oldParams, newParams);
55407             }
55408
55409             function computedHash() {
55410                 return '#' + utilQsString(computedHashParameters(), true);
55411             }
55412
55413             function computedTitle(includeChangeCount) {
55414
55415                 var baseTitle = context.documentTitleBase() || 'iD';
55416                 var contextual;
55417                 var changeCount;
55418                 var titleID;
55419
55420                 var selected = context.selectedIDs().filter(function(id) {
55421                     return context.hasEntity(id);
55422                 });
55423                 if (selected.length) {
55424                     var firstLabel = utilDisplayLabel(context.entity(selected[0]), context.graph());
55425                     if (selected.length > 1 ) {
55426                         contextual = _t('title.labeled_and_more', {
55427                             labeled: firstLabel,
55428                             count: (selected.length - 1).toString()
55429                         });
55430                     } else {
55431                         contextual = firstLabel;
55432                     }
55433                     titleID = 'context';
55434                 }
55435
55436                 if (includeChangeCount) {
55437                     changeCount = context.history().difference().summary().length;
55438                     if (changeCount > 0) {
55439                         titleID = contextual ? 'changes_context' : 'changes';
55440                     }
55441                 }
55442
55443                 if (titleID) {
55444                     return _t('title.format.' + titleID, {
55445                         changes: changeCount,
55446                         base: baseTitle,
55447                         context: contextual
55448                     });
55449                 }
55450
55451                 return baseTitle;
55452             }
55453
55454             function updateTitle(includeChangeCount) {
55455                 if (!context.setsDocumentTitle()) { return; }
55456
55457                 var newTitle = computedTitle(includeChangeCount);
55458                 if (document.title !== newTitle) {
55459                     document.title = newTitle;
55460                 }
55461             }
55462
55463             function updateHashIfNeeded() {
55464                 if (context.inIntro()) { return; }
55465
55466                 var latestHash = computedHash();
55467                 if (_cachedHash !== latestHash) {
55468                     _cachedHash = latestHash;
55469
55470                     // Update the URL hash without affecting the browser navigation stack,
55471                     // though unavoidably creating a browser history entry
55472                     window.history.replaceState(null, computedTitle(false /* includeChangeCount */), latestHash);
55473
55474                     // set the title we want displayed for the browser tab/window
55475                     updateTitle(true /* includeChangeCount */);
55476                 }
55477             }
55478
55479             var _throttledUpdate = throttle(updateHashIfNeeded, 500);
55480             var _throttledUpdateTitle = throttle(function() {
55481                 updateTitle(true /* includeChangeCount */);
55482             }, 500);
55483
55484             function hashchange() {
55485
55486                 // ignore spurious hashchange events
55487                 if (window.location.hash === _cachedHash) { return; }
55488
55489                 _cachedHash = window.location.hash;
55490
55491                 var q = utilStringQs(_cachedHash);
55492                 var mapArgs = (q.map || '').split('/').map(Number);
55493
55494                 if (mapArgs.length < 3 || mapArgs.some(isNaN)) {
55495                     // replace bogus hash
55496                     updateHashIfNeeded();
55497
55498                 } else {
55499                     // don't update if the new hash already reflects the state of iD
55500                     if (_cachedHash === computedHash()) { return; }
55501
55502                     var mode = context.mode();
55503
55504                     context.map().centerZoom([mapArgs[2], Math.min(_latitudeLimit, Math.max(-_latitudeLimit, mapArgs[1]))], mapArgs[0]);
55505
55506                     if (q.id) {
55507                         var ids = q.id.split(',').filter(function(id) {
55508                             return context.hasEntity(id);
55509                         });
55510                         var skip = mode && mode.id === 'select' && utilArrayIdentical(mode.selectedIDs(), ids);
55511                         if (ids.length && !skip) {
55512                             context.enter(modeSelect(context, ids));
55513                             return;
55514                         }
55515                     }
55516
55517                     var center = context.map().center();
55518                     var dist = geoSphericalDistance(center, [mapArgs[2], mapArgs[1]]);
55519                     var maxdist = 500;
55520
55521                     // Don't allow the hash location to change too much while drawing
55522                     // This can happen if the user accidently hit the back button.  #3996
55523                     if (mode && mode.id.match(/^draw/) !== null && dist > maxdist) {
55524                         context.enter(modeBrowse(context));
55525                         return;
55526                     }
55527                 }
55528             }
55529
55530             function behavior() {
55531                 context.map()
55532                     .on('move.behaviorHash', _throttledUpdate);
55533
55534                 context.history()
55535                     .on('change.behaviorHash', _throttledUpdateTitle);
55536
55537                 context
55538                     .on('enter.behaviorHash', _throttledUpdate);
55539
55540                 select(window)
55541                     .on('hashchange.behaviorHash', hashchange);
55542
55543                 if (window.location.hash) {
55544                     var q = utilStringQs(window.location.hash);
55545
55546                     if (q.id) {
55547                         //if (!context.history().hasRestorableChanges()) {
55548                             // targeting specific features: download, select, and zoom to them
55549                             context.zoomToEntity(q.id.split(',')[0], !q.map);
55550                         //}
55551                     }
55552
55553                     if (q.walkthrough === 'true') {
55554                         behavior.startWalkthrough = true;
55555                     }
55556
55557                     if (q.map) {
55558                         behavior.hadHash = true;
55559                     }
55560
55561                     hashchange();
55562
55563                     updateTitle(false);
55564                 }
55565             }
55566
55567             behavior.off = function() {
55568                 _throttledUpdate.cancel();
55569                 _throttledUpdateTitle.cancel();
55570
55571                 context.map()
55572                     .on('move.behaviorHash', null);
55573
55574                 context
55575                     .on('enter.behaviorHash', null);
55576
55577                 select(window)
55578                     .on('hashchange.behaviorHash', null);
55579
55580                 window.location.hash = '';
55581             };
55582
55583             return behavior;
55584         }
55585
55586         /*
55587             iD.coreDifference represents the difference between two graphs.
55588             It knows how to calculate the set of entities that were
55589             created, modified, or deleted, and also contains the logic
55590             for recursively extending a difference to the complete set
55591             of entities that will require a redraw, taking into account
55592             child and parent relationships.
55593          */
55594         function coreDifference(base, head) {
55595             var _changes = {};
55596             var _didChange = {};  // 'addition', 'deletion', 'geometry', 'properties'
55597             var _diff = {};
55598
55599             function checkEntityID(id) {
55600                 var h = head.entities[id];
55601                 var b = base.entities[id];
55602
55603                 if (h === b) { return; }
55604                 if (_changes[id]) { return; }
55605
55606                 if (!h && b) {
55607                     _changes[id] = { base: b, head: h };
55608                     _didChange.deletion = true;
55609                     return;
55610                 }
55611                 if (h && !b) {
55612                     _changes[id] = { base: b, head: h };
55613                     _didChange.addition = true;
55614                     return;
55615                 }
55616
55617                 if (h && b) {
55618                     if (h.members && b.members && !fastDeepEqual(h.members, b.members)) {
55619                         _changes[id] = { base: b, head: h };
55620                         _didChange.geometry = true;
55621                         _didChange.properties = true;
55622                         return;
55623                     }
55624                     if (h.loc && b.loc && !geoVecEqual(h.loc, b.loc)) {
55625                         _changes[id] = { base: b, head: h };
55626                         _didChange.geometry = true;
55627                     }
55628                     if (h.nodes && b.nodes && !fastDeepEqual(h.nodes, b.nodes)) {
55629                         _changes[id] = { base: b, head: h };
55630                         _didChange.geometry = true;
55631                     }
55632                     if (h.tags && b.tags && !fastDeepEqual(h.tags, b.tags)) {
55633                         _changes[id] = { base: b, head: h };
55634                         _didChange.properties = true;
55635                     }
55636                 }
55637             }
55638
55639             function load() {
55640                 // HOT CODE: there can be many thousands of downloaded entities, so looping
55641                 // through them all can become a performance bottleneck. Optimize by
55642                 // resolving duplicates and using a basic `for` loop
55643                 var ids = utilArrayUniq(Object.keys(head.entities).concat(Object.keys(base.entities)));
55644                 for (var i = 0; i < ids.length; i++) {
55645                     checkEntityID(ids[i]);
55646                 }
55647             }
55648             load();
55649
55650
55651             _diff.length = function length() {
55652                 return Object.keys(_changes).length;
55653             };
55654
55655
55656             _diff.changes = function changes() {
55657                 return _changes;
55658             };
55659
55660             _diff.didChange = _didChange;
55661
55662
55663             // pass true to include affected relation members
55664             _diff.extantIDs = function extantIDs(includeRelMembers) {
55665                 var result = new Set();
55666                 Object.keys(_changes).forEach(function(id) {
55667                     if (_changes[id].head) {
55668                         result.add(id);
55669                     }
55670
55671                     var h = _changes[id].head;
55672                     var b = _changes[id].base;
55673                     var entity = h || b;
55674
55675                     if (includeRelMembers && entity.type === 'relation') {
55676                         var mh = h ? h.members.map(function(m) { return m.id; }) : [];
55677                         var mb = b ? b.members.map(function(m) { return m.id; }) : [];
55678                         utilArrayUnion(mh, mb).forEach(function(memberID) {
55679                             if (head.hasEntity(memberID)) {
55680                                 result.add(memberID);
55681                             }
55682                         });
55683                     }
55684                 });
55685
55686                 return Array.from(result);
55687             };
55688
55689
55690             _diff.modified = function modified() {
55691                 var result = [];
55692                 Object.values(_changes).forEach(function(change) {
55693                     if (change.base && change.head) {
55694                         result.push(change.head);
55695                     }
55696                 });
55697                 return result;
55698             };
55699
55700
55701             _diff.created = function created() {
55702                 var result = [];
55703                 Object.values(_changes).forEach(function(change) {
55704                     if (!change.base && change.head) {
55705                         result.push(change.head);
55706                     }
55707                 });
55708                 return result;
55709             };
55710
55711
55712             _diff.deleted = function deleted() {
55713                 var result = [];
55714                 Object.values(_changes).forEach(function(change) {
55715                     if (change.base && !change.head) {
55716                         result.push(change.base);
55717                     }
55718                 });
55719                 return result;
55720             };
55721
55722
55723             _diff.summary = function summary() {
55724                 var relevant = {};
55725
55726                 var keys = Object.keys(_changes);
55727                 for (var i = 0; i < keys.length; i++) {
55728                     var change = _changes[keys[i]];
55729
55730                     if (change.head && change.head.geometry(head) !== 'vertex') {
55731                         addEntity(change.head, head, change.base ? 'modified' : 'created');
55732
55733                     } else if (change.base && change.base.geometry(base) !== 'vertex') {
55734                         addEntity(change.base, base, 'deleted');
55735
55736                     } else if (change.base && change.head) { // modified vertex
55737                         var moved    = !fastDeepEqual(change.base.loc,  change.head.loc);
55738                         var retagged = !fastDeepEqual(change.base.tags, change.head.tags);
55739
55740                         if (moved) {
55741                             addParents(change.head);
55742                         }
55743
55744                         if (retagged || (moved && change.head.hasInterestingTags())) {
55745                             addEntity(change.head, head, 'modified');
55746                         }
55747
55748                     } else if (change.head && change.head.hasInterestingTags()) { // created vertex
55749                         addEntity(change.head, head, 'created');
55750
55751                     } else if (change.base && change.base.hasInterestingTags()) { // deleted vertex
55752                         addEntity(change.base, base, 'deleted');
55753                     }
55754                 }
55755
55756                 return Object.values(relevant);
55757
55758
55759                 function addEntity(entity, graph, changeType) {
55760                     relevant[entity.id] = {
55761                         entity: entity,
55762                         graph: graph,
55763                         changeType: changeType
55764                     };
55765                 }
55766
55767                 function addParents(entity) {
55768                     var parents = head.parentWays(entity);
55769                     for (var j = parents.length - 1; j >= 0; j--) {
55770                         var parent = parents[j];
55771                         if (!(parent.id in relevant)) {
55772                             addEntity(parent, head, 'modified');
55773                         }
55774                     }
55775                 }
55776             };
55777
55778
55779             // returns complete set of entities that require a redraw
55780             //  (optionally within given `extent`)
55781             _diff.complete = function complete(extent) {
55782                 var result = {};
55783                 var id, change;
55784
55785                 for (id in _changes) {
55786                     change = _changes[id];
55787
55788                     var h = change.head;
55789                     var b = change.base;
55790                     var entity = h || b;
55791                     var i;
55792
55793                     if (extent &&
55794                         (!h || !h.intersects(extent, head)) &&
55795                         (!b || !b.intersects(extent, base)))
55796                         { continue; }
55797
55798                     result[id] = h;
55799
55800                     if (entity.type === 'way') {
55801                         var nh = h ? h.nodes : [];
55802                         var nb = b ? b.nodes : [];
55803                         var diff;
55804
55805                         diff = utilArrayDifference(nh, nb);
55806                         for (i = 0; i < diff.length; i++) {
55807                             result[diff[i]] = head.hasEntity(diff[i]);
55808                         }
55809
55810                         diff = utilArrayDifference(nb, nh);
55811                         for (i = 0; i < diff.length; i++) {
55812                             result[diff[i]] = head.hasEntity(diff[i]);
55813                         }
55814                     }
55815
55816                     if (entity.type === 'relation' && entity.isMultipolygon()) {
55817                         var mh = h ? h.members.map(function(m) { return m.id; }) : [];
55818                         var mb = b ? b.members.map(function(m) { return m.id; }) : [];
55819                         var ids = utilArrayUnion(mh, mb);
55820                         for (i = 0; i < ids.length; i++) {
55821                             var member = head.hasEntity(ids[i]);
55822                             if (!member) { continue; }   // not downloaded
55823                             if (extent && !member.intersects(extent, head)) { continue; }   // not visible
55824                             result[ids[i]] = member;
55825                         }
55826                     }
55827
55828                     addParents(head.parentWays(entity), result);
55829                     addParents(head.parentRelations(entity), result);
55830                 }
55831
55832                 return result;
55833
55834
55835                 function addParents(parents, result) {
55836                     for (var i = 0; i < parents.length; i++) {
55837                         var parent = parents[i];
55838                         if (parent.id in result) { continue; }
55839
55840                         result[parent.id] = parent;
55841                         addParents(head.parentRelations(parent), result);
55842                     }
55843                 }
55844             };
55845
55846
55847             return _diff;
55848         }
55849
55850         function coreTree(head) {
55851             // tree for entities
55852             var _rtree = new RBush();
55853             var _bboxes = {};
55854
55855             // maintain a separate tree for granular way segments
55856             var _segmentsRTree = new RBush();
55857             var _segmentsBBoxes = {};
55858             var _segmentsByWayId = {};
55859
55860             var tree = {};
55861
55862
55863             function entityBBox(entity) {
55864                 var bbox = entity.extent(head).bbox();
55865                 bbox.id = entity.id;
55866                 _bboxes[entity.id] = bbox;
55867                 return bbox;
55868             }
55869
55870
55871             function segmentBBox(segment) {
55872                 var extent = segment.extent(head);
55873                 // extent can be null if the node entites aren't in the graph for some reason
55874                 if (!extent) { return null; }
55875
55876                 var bbox = extent.bbox();
55877                 bbox.segment = segment;
55878                 _segmentsBBoxes[segment.id] = bbox;
55879                 return bbox;
55880             }
55881
55882
55883             function removeEntity(entity) {
55884                 _rtree.remove(_bboxes[entity.id]);
55885                 delete _bboxes[entity.id];
55886
55887                 if (_segmentsByWayId[entity.id]) {
55888                     _segmentsByWayId[entity.id].forEach(function(segment) {
55889                         _segmentsRTree.remove(_segmentsBBoxes[segment.id]);
55890                         delete _segmentsBBoxes[segment.id];
55891                     });
55892                     delete _segmentsByWayId[entity.id];
55893                 }
55894             }
55895
55896
55897             function loadEntities(entities) {
55898                 _rtree.load(entities.map(entityBBox));
55899
55900                 var segments = [];
55901                 entities.forEach(function(entity) {
55902                     if (entity.segments) {
55903                         var entitySegments = entity.segments(head);
55904                         // cache these to make them easy to remove later
55905                         _segmentsByWayId[entity.id] = entitySegments;
55906                         segments = segments.concat(entitySegments);
55907                     }
55908                 });
55909                 if (segments.length) { _segmentsRTree.load(segments.map(segmentBBox).filter(Boolean)); }
55910             }
55911
55912
55913             function updateParents(entity, insertions, memo) {
55914                 head.parentWays(entity).forEach(function(way) {
55915                     if (_bboxes[way.id]) {
55916                         removeEntity(way);
55917                         insertions[way.id] = way;
55918                     }
55919                     updateParents(way, insertions, memo);
55920                 });
55921
55922                 head.parentRelations(entity).forEach(function(relation) {
55923                     if (memo[entity.id]) { return; }
55924                     memo[entity.id] = true;
55925                     if (_bboxes[relation.id]) {
55926                         removeEntity(relation);
55927                         insertions[relation.id] = relation;
55928                     }
55929                     updateParents(relation, insertions, memo);
55930                 });
55931             }
55932
55933
55934             tree.rebase = function(entities, force) {
55935                 var insertions = {};
55936
55937                 for (var i = 0; i < entities.length; i++) {
55938                     var entity = entities[i];
55939                     if (!entity.visible) { continue; }
55940
55941                     if (head.entities.hasOwnProperty(entity.id) || _bboxes[entity.id]) {
55942                         if (!force) {
55943                             continue;
55944                         } else if (_bboxes[entity.id]) {
55945                             removeEntity(entity);
55946                         }
55947                     }
55948
55949                     insertions[entity.id] = entity;
55950                     updateParents(entity, insertions, {});
55951                 }
55952
55953                 loadEntities(Object.values(insertions));
55954
55955                 return tree;
55956             };
55957
55958
55959             function updateToGraph(graph) {
55960                 if (graph === head) { return; }
55961
55962                 var diff = coreDifference(head, graph);
55963
55964                 head = graph;
55965
55966                 var changed = diff.didChange;
55967                 if (!changed.addition && !changed.deletion && !changed.geometry) { return; }
55968
55969                 var insertions = {};
55970
55971                 if (changed.deletion) {
55972                     diff.deleted().forEach(function(entity) {
55973                         removeEntity(entity);
55974                     });
55975                 }
55976
55977                 if (changed.geometry) {
55978                     diff.modified().forEach(function(entity) {
55979                         removeEntity(entity);
55980                         insertions[entity.id] = entity;
55981                         updateParents(entity, insertions, {});
55982                     });
55983                 }
55984
55985                 if (changed.addition) {
55986                     diff.created().forEach(function(entity) {
55987                         insertions[entity.id] = entity;
55988                     });
55989                 }
55990
55991                 loadEntities(Object.values(insertions));
55992             }
55993
55994             // returns an array of entities with bounding boxes overlapping `extent` for the given `graph`
55995             tree.intersects = function(extent, graph) {
55996                 updateToGraph(graph);
55997                 return _rtree.search(extent.bbox())
55998                     .map(function(bbox) { return graph.entity(bbox.id); });
55999             };
56000
56001             // returns an array of segment objects with bounding boxes overlapping `extent` for the given `graph`
56002             tree.waySegments = function(extent, graph) {
56003                 updateToGraph(graph);
56004                 return _segmentsRTree.search(extent.bbox())
56005                     .map(function(bbox) { return bbox.segment; });
56006             };
56007
56008
56009             return tree;
56010         }
56011
56012         function uiModal(selection, blocking) {
56013           var this$1 = this;
56014
56015           var keybinding = utilKeybinding('modal');
56016           var previous = selection.select('div.modal');
56017           var animate = previous.empty();
56018
56019           previous.transition()
56020             .duration(200)
56021             .style('opacity', 0)
56022             .remove();
56023
56024           var shaded = selection
56025             .append('div')
56026             .attr('class', 'shaded')
56027             .style('opacity', 0);
56028
56029           shaded.close = function () {
56030             shaded
56031               .transition()
56032               .duration(200)
56033               .style('opacity',0)
56034               .remove();
56035
56036             modal
56037               .transition()
56038               .duration(200)
56039               .style('top','0px');
56040
56041             select(document)
56042               .call(keybinding.unbind);
56043           };
56044
56045
56046           var modal = shaded
56047             .append('div')
56048             .attr('class', 'modal fillL');
56049
56050           if (!blocking) {
56051             shaded.on('click.remove-modal', function () {
56052               if (event.target === this$1) {
56053                 shaded.close();
56054               }
56055             });
56056
56057             modal
56058               .append('button')
56059               .attr('class', 'close')
56060               .on('click', shaded.close)
56061               .call(svgIcon('#iD-icon-close'));
56062
56063             keybinding
56064               .on('⌫', shaded.close)
56065               .on('⎋', shaded.close);
56066
56067             select(document)
56068               .call(keybinding);
56069           }
56070
56071           modal
56072             .append('div')
56073             .attr('class', 'content');
56074
56075           if (animate) {
56076             shaded.transition().style('opacity', 1);
56077           } else {
56078             shaded.style('opacity', 1);
56079           }
56080
56081           return shaded;
56082         }
56083
56084         function uiLoading(context) {
56085           var _modalSelection = select(null);
56086           var _message = '';
56087           var _blocking = false;
56088
56089
56090           var loading = function (selection) {
56091             _modalSelection = uiModal(selection, _blocking);
56092
56093             var loadertext = _modalSelection.select('.content')
56094               .classed('loading-modal', true)
56095               .append('div')
56096               .attr('class', 'modal-section fillL');
56097
56098             loadertext
56099               .append('img')
56100               .attr('class', 'loader')
56101               .attr('src', context.imagePath('loader-white.gif'));
56102
56103             loadertext
56104               .append('h3')
56105               .text(_message);
56106
56107             _modalSelection.select('button.close')
56108               .attr('class', 'hide');
56109
56110             return loading;
56111           };
56112
56113
56114           loading.message = function(val) {
56115             if (!arguments.length) { return _message; }
56116             _message = val;
56117             return loading;
56118           };
56119
56120
56121           loading.blocking = function(val) {
56122             if (!arguments.length) { return _blocking; }
56123             _blocking = val;
56124             return loading;
56125           };
56126
56127
56128           loading.close = function () {
56129             _modalSelection.remove();
56130           };
56131
56132
56133           loading.isShown = function () {
56134             return _modalSelection && !_modalSelection.empty() && _modalSelection.node().parentNode;
56135           };
56136
56137
56138           return loading;
56139         }
56140
56141         function coreHistory(context) {
56142             var dispatch$1 = dispatch('change', 'merge', 'restore', 'undone', 'redone');
56143             var lock = utilSessionMutex('lock');
56144
56145             // restorable if iD not open in another window/tab and a saved history exists in localStorage
56146             var _hasUnresolvedRestorableChanges = lock.lock() && !!corePreferences(getKey('saved_history'));
56147
56148             var duration = 150;
56149             var _imageryUsed = [];
56150             var _photoOverlaysUsed = [];
56151             var _checkpoints = {};
56152             var _pausedGraph;
56153             var _stack;
56154             var _index;
56155             var _tree;
56156
56157
56158             // internal _act, accepts list of actions and eased time
56159             function _act(actions, t) {
56160                 actions = Array.prototype.slice.call(actions);
56161
56162                 var annotation;
56163                 if (typeof actions[actions.length - 1] !== 'function') {
56164                     annotation = actions.pop();
56165                 }
56166
56167                 var graph = _stack[_index].graph;
56168                 for (var i = 0; i < actions.length; i++) {
56169                     graph = actions[i](graph, t);
56170                 }
56171
56172                 return {
56173                     graph: graph,
56174                     annotation: annotation,
56175                     imageryUsed: _imageryUsed,
56176                     photoOverlaysUsed: _photoOverlaysUsed,
56177                     transform: context.projection.transform(),
56178                     selectedIDs: context.selectedIDs()
56179                 };
56180             }
56181
56182
56183             // internal _perform with eased time
56184             function _perform(args, t) {
56185                 var previous = _stack[_index].graph;
56186                 _stack = _stack.slice(0, _index + 1);
56187                 var actionResult = _act(args, t);
56188                 _stack.push(actionResult);
56189                 _index++;
56190                 return change(previous);
56191             }
56192
56193
56194             // internal _replace with eased time
56195             function _replace(args, t) {
56196                 var previous = _stack[_index].graph;
56197                 // assert(_index == _stack.length - 1)
56198                 var actionResult = _act(args, t);
56199                 _stack[_index] = actionResult;
56200                 return change(previous);
56201             }
56202
56203
56204             // internal _overwrite with eased time
56205             function _overwrite(args, t) {
56206                 var previous = _stack[_index].graph;
56207                 if (_index > 0) {
56208                     _index--;
56209                     _stack.pop();
56210                 }
56211                 _stack = _stack.slice(0, _index + 1);
56212                 var actionResult = _act(args, t);
56213                 _stack.push(actionResult);
56214                 _index++;
56215                 return change(previous);
56216             }
56217
56218
56219             // determine difference and dispatch a change event
56220             function change(previous) {
56221                 var difference = coreDifference(previous, history.graph());
56222                 if (!_pausedGraph) {
56223                     dispatch$1.call('change', this, difference);
56224                 }
56225                 return difference;
56226             }
56227
56228
56229             // iD uses namespaced keys so multiple installations do not conflict
56230             function getKey(n) {
56231                 return 'iD_' + window.location.origin + '_' + n;
56232             }
56233
56234
56235             var history = {
56236
56237                 graph: function() {
56238                     return _stack[_index].graph;
56239                 },
56240
56241
56242                 tree: function() {
56243                     return _tree;
56244                 },
56245
56246
56247                 base: function() {
56248                     return _stack[0].graph;
56249                 },
56250
56251
56252                 merge: function(entities/*, extent*/) {
56253                     var stack = _stack.map(function(state) { return state.graph; });
56254                     _stack[0].graph.rebase(entities, stack, false);
56255                     _tree.rebase(entities, false);
56256
56257                     dispatch$1.call('merge', this, entities);
56258                 },
56259
56260
56261                 perform: function() {
56262                     // complete any transition already in progress
56263                     select(document).interrupt('history.perform');
56264
56265                     var transitionable = false;
56266                     var action0 = arguments[0];
56267
56268                     if (arguments.length === 1 ||
56269                         (arguments.length === 2 && (typeof arguments[1] !== 'function'))) {
56270                         transitionable = !!action0.transitionable;
56271                     }
56272
56273                     if (transitionable) {
56274                         var origArguments = arguments;
56275                         select(document)
56276                             .transition('history.perform')
56277                             .duration(duration)
56278                             .ease(linear$1)
56279                             .tween('history.tween', function() {
56280                                 return function(t) {
56281                                     if (t < 1) { _overwrite([action0], t); }
56282                                 };
56283                             })
56284                             .on('start', function() {
56285                                 _perform([action0], 0);
56286                             })
56287                             .on('end interrupt', function() {
56288                                 _overwrite(origArguments, 1);
56289                             });
56290
56291                     } else {
56292                         return _perform(arguments);
56293                     }
56294                 },
56295
56296
56297                 replace: function() {
56298                     select(document).interrupt('history.perform');
56299                     return _replace(arguments, 1);
56300                 },
56301
56302
56303                 // Same as calling pop and then perform
56304                 overwrite: function() {
56305                     select(document).interrupt('history.perform');
56306                     return _overwrite(arguments, 1);
56307                 },
56308
56309
56310                 pop: function(n) {
56311                     select(document).interrupt('history.perform');
56312
56313                     var previous = _stack[_index].graph;
56314                     if (isNaN(+n) || +n < 0) {
56315                         n = 1;
56316                     }
56317                     while (n-- > 0 && _index > 0) {
56318                         _index--;
56319                         _stack.pop();
56320                     }
56321                     return change(previous);
56322                 },
56323
56324
56325                 // Back to the previous annotated state or _index = 0.
56326                 undo: function() {
56327                     select(document).interrupt('history.perform');
56328
56329                     var previousStack = _stack[_index];
56330                     var previous = previousStack.graph;
56331                     while (_index > 0) {
56332                         _index--;
56333                         if (_stack[_index].annotation) { break; }
56334                     }
56335
56336                     dispatch$1.call('undone', this, _stack[_index], previousStack);
56337                     return change(previous);
56338                 },
56339
56340
56341                 // Forward to the next annotated state.
56342                 redo: function() {
56343                     select(document).interrupt('history.perform');
56344
56345                     var previousStack = _stack[_index];
56346                     var previous = previousStack.graph;
56347                     var tryIndex = _index;
56348                     while (tryIndex < _stack.length - 1) {
56349                         tryIndex++;
56350                         if (_stack[tryIndex].annotation) {
56351                             _index = tryIndex;
56352                             dispatch$1.call('redone', this, _stack[_index], previousStack);
56353                             break;
56354                         }
56355                     }
56356
56357                     return change(previous);
56358                 },
56359
56360
56361                 pauseChangeDispatch: function() {
56362                     if (!_pausedGraph) {
56363                         _pausedGraph = _stack[_index].graph;
56364                     }
56365                 },
56366
56367
56368                 resumeChangeDispatch: function() {
56369                     if (_pausedGraph) {
56370                         var previous = _pausedGraph;
56371                         _pausedGraph = null;
56372                         return change(previous);
56373                     }
56374                 },
56375
56376
56377                 undoAnnotation: function() {
56378                     var i = _index;
56379                     while (i >= 0) {
56380                         if (_stack[i].annotation) { return _stack[i].annotation; }
56381                         i--;
56382                     }
56383                 },
56384
56385
56386                 redoAnnotation: function() {
56387                     var i = _index + 1;
56388                     while (i <= _stack.length - 1) {
56389                         if (_stack[i].annotation) { return _stack[i].annotation; }
56390                         i++;
56391                     }
56392                 },
56393
56394
56395                 // Returns the entities from the active graph with bounding boxes
56396                 // overlapping the given `extent`.
56397                 intersects: function(extent) {
56398                     return _tree.intersects(extent, _stack[_index].graph);
56399                 },
56400
56401
56402                 difference: function() {
56403                     var base = _stack[0].graph;
56404                     var head = _stack[_index].graph;
56405                     return coreDifference(base, head);
56406                 },
56407
56408
56409                 changes: function(action) {
56410                     var base = _stack[0].graph;
56411                     var head = _stack[_index].graph;
56412
56413                     if (action) {
56414                         head = action(head);
56415                     }
56416
56417                     var difference = coreDifference(base, head);
56418
56419                     return {
56420                         modified: difference.modified(),
56421                         created: difference.created(),
56422                         deleted: difference.deleted()
56423                     };
56424                 },
56425
56426
56427                 hasChanges: function() {
56428                     return this.difference().length() > 0;
56429                 },
56430
56431
56432                 imageryUsed: function(sources) {
56433                     if (sources) {
56434                         _imageryUsed = sources;
56435                         return history;
56436                     } else {
56437                         var s = new Set();
56438                         _stack.slice(1, _index + 1).forEach(function(state) {
56439                             state.imageryUsed.forEach(function(source) {
56440                                 if (source !== 'Custom') {
56441                                     s.add(source);
56442                                 }
56443                             });
56444                         });
56445                         return Array.from(s);
56446                     }
56447                 },
56448
56449
56450                 photoOverlaysUsed: function(sources) {
56451                     if (sources) {
56452                         _photoOverlaysUsed = sources;
56453                         return history;
56454                     } else {
56455                         var s = new Set();
56456                         _stack.slice(1, _index + 1).forEach(function(state) {
56457                             if (state.photoOverlaysUsed && Array.isArray(state.photoOverlaysUsed)) {
56458                                 state.photoOverlaysUsed.forEach(function(photoOverlay) {
56459                                     s.add(photoOverlay);
56460                                 });
56461                             }
56462                         });
56463                         return Array.from(s);
56464                     }
56465                 },
56466
56467
56468                 // save the current history state
56469                 checkpoint: function(key) {
56470                     _checkpoints[key] = {
56471                         stack: _stack,
56472                         index: _index
56473                     };
56474                     return history;
56475                 },
56476
56477
56478                 // restore history state to a given checkpoint or reset completely
56479                 reset: function(key) {
56480                     if (key !== undefined && _checkpoints.hasOwnProperty(key)) {
56481                         _stack = _checkpoints[key].stack;
56482                         _index = _checkpoints[key].index;
56483                     } else {
56484                         _stack = [{graph: coreGraph()}];
56485                         _index = 0;
56486                         _tree = coreTree(_stack[0].graph);
56487                         _checkpoints = {};
56488                     }
56489                     dispatch$1.call('change');
56490                     return history;
56491                 },
56492
56493
56494                 // `toIntroGraph()` is used to export the intro graph used by the walkthrough.
56495                 //
56496                 // To use it:
56497                 //  1. Start the walkthrough.
56498                 //  2. Get to a "free editing" tutorial step
56499                 //  3. Make your edits to the walkthrough map
56500                 //  4. In your browser dev console run:
56501                 //        `id.history().toIntroGraph()`
56502                 //  5. This outputs stringified JSON to the browser console
56503                 //  6. Copy it to `data/intro_graph.json` and prettify it in your code editor
56504                 toIntroGraph: function() {
56505                     var nextID = { n: 0, r: 0, w: 0 };
56506                     var permIDs = {};
56507                     var graph = this.graph();
56508                     var baseEntities = {};
56509
56510                     // clone base entities..
56511                     Object.values(graph.base().entities).forEach(function(entity) {
56512                         var copy = copyIntroEntity(entity);
56513                         baseEntities[copy.id] = copy;
56514                     });
56515
56516                     // replace base entities with head entities..
56517                     Object.keys(graph.entities).forEach(function(id) {
56518                         var entity = graph.entities[id];
56519                         if (entity) {
56520                             var copy = copyIntroEntity(entity);
56521                             baseEntities[copy.id] = copy;
56522                         } else {
56523                             delete baseEntities[id];
56524                         }
56525                     });
56526
56527                     // swap temporary for permanent ids..
56528                     Object.values(baseEntities).forEach(function(entity) {
56529                         if (Array.isArray(entity.nodes)) {
56530                             entity.nodes = entity.nodes.map(function(node) {
56531                                 return permIDs[node] || node;
56532                             });
56533                         }
56534                         if (Array.isArray(entity.members)) {
56535                             entity.members = entity.members.map(function(member) {
56536                                 member.id = permIDs[member.id] || member.id;
56537                                 return member;
56538                             });
56539                         }
56540                     });
56541
56542                     return JSON.stringify({ dataIntroGraph: baseEntities });
56543
56544
56545                     function copyIntroEntity(source) {
56546                         var copy = utilObjectOmit(source, ['type', 'user', 'v', 'version', 'visible']);
56547
56548                         // Note: the copy is no longer an osmEntity, so it might not have `tags`
56549                         if (copy.tags && !Object.keys(copy.tags)) {
56550                             delete copy.tags;
56551                         }
56552
56553                         if (Array.isArray(copy.loc)) {
56554                             copy.loc[0] = +copy.loc[0].toFixed(6);
56555                             copy.loc[1] = +copy.loc[1].toFixed(6);
56556                         }
56557
56558                         var match = source.id.match(/([nrw])-\d*/);  // temporary id
56559                         if (match !== null) {
56560                             var nrw = match[1];
56561                             var permID;
56562                             do { permID = nrw + (++nextID[nrw]); }
56563                             while (baseEntities.hasOwnProperty(permID));
56564
56565                             copy.id = permIDs[source.id] = permID;
56566                         }
56567                         return copy;
56568                     }
56569                 },
56570
56571
56572                 toJSON: function() {
56573                     if (!this.hasChanges()) { return; }
56574
56575                     var allEntities = {};
56576                     var baseEntities = {};
56577                     var base = _stack[0];
56578
56579                     var s = _stack.map(function(i) {
56580                         var modified = [];
56581                         var deleted = [];
56582
56583                         Object.keys(i.graph.entities).forEach(function(id) {
56584                             var entity = i.graph.entities[id];
56585                             if (entity) {
56586                                 var key = osmEntity.key(entity);
56587                                 allEntities[key] = entity;
56588                                 modified.push(key);
56589                             } else {
56590                                 deleted.push(id);
56591                             }
56592
56593                             // make sure that the originals of changed or deleted entities get merged
56594                             // into the base of the _stack after restoring the data from JSON.
56595                             if (id in base.graph.entities) {
56596                                 baseEntities[id] = base.graph.entities[id];
56597                             }
56598                             if (entity && entity.nodes) {
56599                                 // get originals of pre-existing child nodes
56600                                 entity.nodes.forEach(function(nodeID) {
56601                                     if (nodeID in base.graph.entities) {
56602                                         baseEntities[nodeID] = base.graph.entities[nodeID];
56603                                     }
56604                                 });
56605                             }
56606                             // get originals of parent entities too
56607                             var baseParents = base.graph._parentWays[id];
56608                             if (baseParents) {
56609                                 baseParents.forEach(function(parentID) {
56610                                     if (parentID in base.graph.entities) {
56611                                         baseEntities[parentID] = base.graph.entities[parentID];
56612                                     }
56613                                 });
56614                             }
56615                         });
56616
56617                         var x = {};
56618
56619                         if (modified.length) { x.modified = modified; }
56620                         if (deleted.length) { x.deleted = deleted; }
56621                         if (i.imageryUsed) { x.imageryUsed = i.imageryUsed; }
56622                         if (i.photoOverlaysUsed) { x.photoOverlaysUsed = i.photoOverlaysUsed; }
56623                         if (i.annotation) { x.annotation = i.annotation; }
56624                         if (i.transform) { x.transform = i.transform; }
56625                         if (i.selectedIDs) { x.selectedIDs = i.selectedIDs; }
56626
56627                         return x;
56628                     });
56629
56630                     return JSON.stringify({
56631                         version: 3,
56632                         entities: Object.values(allEntities),
56633                         baseEntities: Object.values(baseEntities),
56634                         stack: s,
56635                         nextIDs: osmEntity.id.next,
56636                         index: _index,
56637                         // note the time the changes were saved
56638                         timestamp: (new Date()).getTime()
56639                     });
56640                 },
56641
56642
56643                 fromJSON: function(json, loadChildNodes) {
56644                     var h = JSON.parse(json);
56645                     var loadComplete = true;
56646
56647                     osmEntity.id.next = h.nextIDs;
56648                     _index = h.index;
56649
56650                     if (h.version === 2 || h.version === 3) {
56651                         var allEntities = {};
56652
56653                         h.entities.forEach(function(entity) {
56654                             allEntities[osmEntity.key(entity)] = osmEntity(entity);
56655                         });
56656
56657                         if (h.version === 3) {
56658                             // This merges originals for changed entities into the base of
56659                             // the _stack even if the current _stack doesn't have them (for
56660                             // example when iD has been restarted in a different region)
56661                             var baseEntities = h.baseEntities.map(function(d) { return osmEntity(d); });
56662                             var stack = _stack.map(function(state) { return state.graph; });
56663                             _stack[0].graph.rebase(baseEntities, stack, true);
56664                             _tree.rebase(baseEntities, true);
56665
56666                             // When we restore a modified way, we also need to fetch any missing
56667                             // childnodes that would normally have been downloaded with it.. #2142
56668                             if (loadChildNodes) {
56669                                 var osm = context.connection();
56670                                 var baseWays = baseEntities
56671                                     .filter(function(e) { return e.type === 'way'; });
56672                                 var nodeIDs = baseWays
56673                                     .reduce(function(acc, way) { return utilArrayUnion(acc, way.nodes); }, []);
56674                                 var missing = nodeIDs
56675                                     .filter(function(n) { return !_stack[0].graph.hasEntity(n); });
56676
56677                                 if (missing.length && osm) {
56678                                     loadComplete = false;
56679                                     context.map().redrawEnable(false);
56680
56681                                     var loading = uiLoading(context).blocking(true);
56682                                     context.container().call(loading);
56683
56684                                     var childNodesLoaded = function(err, result) {
56685                                         if (!err) {
56686                                             var visibleGroups = utilArrayGroupBy(result.data, 'visible');
56687                                             var visibles = visibleGroups.true || [];      // alive nodes
56688                                             var invisibles = visibleGroups.false || [];   // deleted nodes
56689
56690                                             if (visibles.length) {
56691                                                 var visibleIDs = visibles.map(function(entity) { return entity.id; });
56692                                                 var stack = _stack.map(function(state) { return state.graph; });
56693                                                 missing = utilArrayDifference(missing, visibleIDs);
56694                                                 _stack[0].graph.rebase(visibles, stack, true);
56695                                                 _tree.rebase(visibles, true);
56696                                             }
56697
56698                                             // fetch older versions of nodes that were deleted..
56699                                             invisibles.forEach(function(entity) {
56700                                                 osm.loadEntityVersion(entity.id, +entity.version - 1, childNodesLoaded);
56701                                             });
56702                                         }
56703
56704                                         if (err || !missing.length) {
56705                                             loading.close();
56706                                             context.map().redrawEnable(true);
56707                                             dispatch$1.call('change');
56708                                             dispatch$1.call('restore', this);
56709                                         }
56710                                     };
56711
56712                                     osm.loadMultiple(missing, childNodesLoaded);
56713                                 }
56714                             }
56715                         }
56716
56717                         _stack = h.stack.map(function(d) {
56718                             var entities = {}, entity;
56719
56720                             if (d.modified) {
56721                                 d.modified.forEach(function(key) {
56722                                     entity = allEntities[key];
56723                                     entities[entity.id] = entity;
56724                                 });
56725                             }
56726
56727                             if (d.deleted) {
56728                                 d.deleted.forEach(function(id) {
56729                                     entities[id] = undefined;
56730                                 });
56731                             }
56732
56733                             return {
56734                                 graph: coreGraph(_stack[0].graph).load(entities),
56735                                 annotation: d.annotation,
56736                                 imageryUsed: d.imageryUsed,
56737                                 photoOverlaysUsed: d.photoOverlaysUsed,
56738                                 transform: d.transform,
56739                                 selectedIDs: d.selectedIDs
56740                             };
56741                         });
56742
56743                     } else { // original version
56744                         _stack = h.stack.map(function(d) {
56745                             var entities = {};
56746
56747                             for (var i in d.entities) {
56748                                 var entity = d.entities[i];
56749                                 entities[i] = entity === 'undefined' ? undefined : osmEntity(entity);
56750                             }
56751
56752                             d.graph = coreGraph(_stack[0].graph).load(entities);
56753                             return d;
56754                         });
56755                     }
56756
56757                     var transform = _stack[_index].transform;
56758                     if (transform) {
56759                         context.map().transformEase(transform, 0);   // 0 = immediate, no easing
56760                     }
56761
56762                     if (loadComplete) {
56763                         dispatch$1.call('change');
56764                         dispatch$1.call('restore', this);
56765                     }
56766
56767                     return history;
56768                 },
56769
56770
56771                 lock: function() {
56772                     return lock.lock();
56773                 },
56774
56775
56776                 unlock: function() {
56777                     lock.unlock();
56778                 },
56779
56780
56781                 save: function() {
56782                     if (lock.locked() &&
56783                         // don't overwrite existing, unresolved changes
56784                         !_hasUnresolvedRestorableChanges) {
56785
56786                         corePreferences(getKey('saved_history'), history.toJSON() || null);
56787                     }
56788                     return history;
56789                 },
56790
56791
56792                 // delete the history version saved in localStorage
56793                 clearSaved: function() {
56794                     context.debouncedSave.cancel();
56795                     if (lock.locked()) {
56796                         _hasUnresolvedRestorableChanges = false;
56797                         corePreferences(getKey('saved_history'), null);
56798
56799                         // clear the changeset metadata associated with the saved history
56800                         corePreferences('comment', null);
56801                         corePreferences('hashtags', null);
56802                         corePreferences('source', null);
56803                     }
56804                     return history;
56805                 },
56806
56807
56808                 savedHistoryJSON: function() {
56809                     return corePreferences(getKey('saved_history'));
56810                 },
56811
56812
56813                 hasRestorableChanges: function() {
56814                     return _hasUnresolvedRestorableChanges;
56815                 },
56816
56817
56818                 // load history from a version stored in localStorage
56819                 restore: function() {
56820                     if (lock.locked()) {
56821                         _hasUnresolvedRestorableChanges = false;
56822                         var json = this.savedHistoryJSON();
56823                         if (json) { history.fromJSON(json, true); }
56824                     }
56825                 },
56826
56827
56828                 _getKey: getKey
56829
56830             };
56831
56832
56833             history.reset();
56834
56835             return utilRebind(history, dispatch$1, 'on');
56836         }
56837
56838         /**
56839          * Look for roads that can be connected to other roads with a short extension
56840          */
56841         function validationAlmostJunction(context) {
56842           var type = 'almost_junction';
56843           var EXTEND_TH_METERS = 5;
56844           var WELD_TH_METERS = 0.75;
56845           // Comes from considering bounding case of parallel ways
56846           var CLOSE_NODE_TH = EXTEND_TH_METERS - WELD_TH_METERS;
56847           // Comes from considering bounding case of perpendicular ways
56848           var SIG_ANGLE_TH = Math.atan(WELD_TH_METERS / EXTEND_TH_METERS);
56849
56850           function isHighway(entity) {
56851             return entity.type === 'way'
56852               && osmRoutableHighwayTagValues[entity.tags.highway];
56853           }
56854
56855           function isTaggedAsNotContinuing(node) {
56856             return node.tags.noexit === 'yes'
56857               || node.tags.amenity === 'parking_entrance'
56858               || (node.tags.entrance && node.tags.entrance !== 'no');
56859           }
56860
56861
56862           var validation = function checkAlmostJunction(entity, graph) {
56863             if (!isHighway(entity)) { return []; }
56864             if (entity.isDegenerate()) { return []; }
56865
56866             var tree = context.history().tree();
56867             var extendableNodeInfos = findConnectableEndNodesByExtension(entity);
56868
56869             var issues = [];
56870
56871             extendableNodeInfos.forEach(function (extendableNodeInfo) {
56872               issues.push(new validationIssue({
56873                 type: type,
56874                 subtype: 'highway-highway',
56875                 severity: 'warning',
56876                 message: function message(context) {
56877                   var entity1 = context.hasEntity(this.entityIds[0]);
56878                   if (this.entityIds[0] === this.entityIds[2]) {
56879                     return entity1 ? _t('issues.almost_junction.self.message', {
56880                       feature: utilDisplayLabel(entity1, context.graph())
56881                     }) : '';
56882                   } else {
56883                     var entity2 = context.hasEntity(this.entityIds[2]);
56884                     return (entity1 && entity2) ? _t('issues.almost_junction.message', {
56885                       feature: utilDisplayLabel(entity1, context.graph()),
56886                       feature2: utilDisplayLabel(entity2, context.graph())
56887                     }) : '';
56888                   }
56889                 },
56890                 reference: showReference,
56891                 entityIds: [
56892                   entity.id,
56893                   extendableNodeInfo.node.id,
56894                   extendableNodeInfo.wid ],
56895                 loc: extendableNodeInfo.node.loc,
56896                 hash: JSON.stringify(extendableNodeInfo.node.loc),
56897                 data: {
56898                   midId: extendableNodeInfo.mid.id,
56899                   edge: extendableNodeInfo.edge,
56900                   cross_loc: extendableNodeInfo.cross_loc
56901                 },
56902                 dynamicFixes: makeFixes
56903               }));
56904             });
56905
56906             return issues;
56907
56908             function makeFixes(context) {
56909               var fixes = [new validationIssueFix({
56910                 icon: 'iD-icon-abutment',
56911                 title: _t('issues.fix.connect_features.title'),
56912                 onClick: function onClick(context) {
56913                   var annotation = _t('issues.fix.connect_almost_junction.annotation');
56914                   var ref = this.issue.entityIds;
56915                   var endNodeId = ref[1];
56916                   var crossWayId = ref[2];
56917                   var midNode = context.entity(this.issue.data.midId);
56918                   var endNode = context.entity(endNodeId);
56919                   var crossWay = context.entity(crossWayId);
56920
56921                   // When endpoints are close, just join if resulting small change in angle (#7201)
56922                   var nearEndNodes = findNearbyEndNodes(endNode, crossWay);
56923                   if (nearEndNodes.length > 0) {
56924                     var collinear = findSmallJoinAngle(midNode, endNode, nearEndNodes);
56925                     if (collinear) {
56926                       context.perform(
56927                         actionMergeNodes([collinear.id, endNode.id], collinear.loc),
56928                         annotation
56929                       );
56930                       return;
56931                     }
56932                   }
56933
56934                   var targetEdge = this.issue.data.edge;
56935                   var crossLoc = this.issue.data.cross_loc;
56936                   var edgeNodes = [context.entity(targetEdge[0]), context.entity(targetEdge[1])];
56937                   var closestNodeInfo = geoSphericalClosestNode(edgeNodes, crossLoc);
56938
56939                   // already a point nearby, just connect to that
56940                   if (closestNodeInfo.distance < WELD_TH_METERS) {
56941                     context.perform(
56942                       actionMergeNodes([closestNodeInfo.node.id, endNode.id], closestNodeInfo.node.loc),
56943                       annotation
56944                     );
56945                   // else add the end node to the edge way
56946                   } else {
56947                     context.perform(
56948                       actionAddMidpoint({loc: crossLoc, edge: targetEdge}, endNode),
56949                       annotation
56950                     );
56951                   }
56952                 }
56953               })];
56954
56955               var node = context.hasEntity(this.entityIds[1]);
56956               if (node && !node.hasInterestingTags()) {
56957                 // node has no descriptive tags, suggest noexit fix
56958                 fixes.push(new validationIssueFix({
56959                   icon: 'maki-barrier',
56960                   title: _t('issues.fix.tag_as_disconnected.title'),
56961                   onClick: function onClick(context) {
56962                     var nodeID = this.issue.entityIds[1];
56963                     var tags = Object.assign({}, context.entity(nodeID).tags);
56964                     tags.noexit = 'yes';
56965                     context.perform(
56966                       actionChangeTags(nodeID, tags),
56967                       _t('issues.fix.tag_as_disconnected.annotation')
56968                     );
56969                   }
56970                 }));
56971               }
56972
56973               return fixes;
56974             }
56975
56976             function showReference(selection) {
56977               selection.selectAll('.issue-reference')
56978                 .data([0])
56979                 .enter()
56980                 .append('div')
56981                 .attr('class', 'issue-reference')
56982                 .text(_t('issues.almost_junction.highway-highway.reference'));
56983             }
56984
56985             function isExtendableCandidate(node, way) {
56986               // can not accurately test vertices on tiles not downloaded from osm - #5938
56987               var osm = services.osm;
56988               if (osm && !osm.isDataLoaded(node.loc)) {
56989                 return false;
56990               }
56991               if (isTaggedAsNotContinuing(node) || graph.parentWays(node).length !== 1) {
56992                 return false;
56993               }
56994
56995               var occurences = 0;
56996               for (var index in way.nodes) {
56997                 if (way.nodes[index] === node.id) {
56998                   occurences += 1;
56999                   if (occurences > 1) {
57000                     return false;
57001                   }
57002                 }
57003               }
57004               return true;
57005             }
57006
57007             function findConnectableEndNodesByExtension(way) {
57008               var results = [];
57009               if (way.isClosed()) { return results; }
57010
57011               var testNodes;
57012               var indices = [0, way.nodes.length - 1];
57013               indices.forEach(function (nodeIndex) {
57014                 var nodeID = way.nodes[nodeIndex];
57015                 var node = graph.entity(nodeID);
57016
57017                 if (!isExtendableCandidate(node, way)) { return; }
57018
57019                 var connectionInfo = canConnectByExtend(way, nodeIndex);
57020                 if (!connectionInfo) { return; }
57021
57022                 testNodes = graph.childNodes(way).slice();   // shallow copy
57023                 testNodes[nodeIndex] = testNodes[nodeIndex].move(connectionInfo.cross_loc);
57024
57025                 // don't flag issue if connecting the ways would cause self-intersection
57026                 if (geoHasSelfIntersections(testNodes, nodeID)) { return; }
57027
57028                 results.push(connectionInfo);
57029               });
57030
57031               return results;
57032             }
57033
57034             function findNearbyEndNodes(node, way) {
57035               return [
57036                 way.nodes[0],
57037                 way.nodes[way.nodes.length - 1]
57038               ].map(function (d) { return graph.entity(d); })
57039               .filter(function (d) {
57040                 // Node cannot be near to itself, but other endnode of same way could be
57041                 return d.id !== node.id
57042                   && geoSphericalDistance(node.loc, d.loc) <= CLOSE_NODE_TH;
57043               });
57044             }
57045
57046             function findSmallJoinAngle(midNode, tipNode, endNodes) {
57047               // Both nodes could be close, so want to join whichever is closest to collinear
57048               var joinTo;
57049               var minAngle = Infinity;
57050
57051               // Checks midNode -> tipNode -> endNode for collinearity
57052               endNodes.forEach(function (endNode) {
57053                 var a1 = geoAngle(midNode, tipNode, context.projection) + Math.PI;
57054                 var a2 = geoAngle(midNode, endNode, context.projection) + Math.PI;
57055                 var diff = Math.max(a1, a2) - Math.min(a1, a2);
57056
57057                 if (diff < minAngle) {
57058                   joinTo = endNode;
57059                   minAngle = diff;
57060                 }
57061               });
57062
57063               /* Threshold set by considering right angle triangle
57064               based on node joining threshold and extension distance */
57065               if (minAngle <= SIG_ANGLE_TH) { return joinTo; }
57066
57067               return null;
57068             }
57069
57070             function hasTag(tags, key) {
57071               return tags[key] !== undefined && tags[key] !== 'no';
57072             }
57073
57074             function canConnectWays(way, way2) {
57075
57076               // allow self-connections
57077               if (way.id === way2.id) { return true; }
57078
57079               // if one is bridge or tunnel, both must be bridge or tunnel
57080               if ((hasTag(way.tags, 'bridge') || hasTag(way2.tags, 'bridge')) &&
57081                 !(hasTag(way.tags, 'bridge') && hasTag(way2.tags, 'bridge'))) { return false; }
57082               if ((hasTag(way.tags, 'tunnel') || hasTag(way2.tags, 'tunnel')) &&
57083                 !(hasTag(way.tags, 'tunnel') && hasTag(way2.tags, 'tunnel'))) { return false; }
57084
57085               // must have equivalent layers and levels
57086               var layer1 = way.tags.layer || '0',
57087                 layer2 = way2.tags.layer || '0';
57088               if (layer1 !== layer2) { return false; }
57089
57090               var level1 = way.tags.level || '0',
57091                 level2 = way2.tags.level || '0';
57092               if (level1 !== level2) { return false; }
57093
57094               return true;
57095             }
57096
57097             function canConnectByExtend(way, endNodeIdx) {
57098               var tipNid = way.nodes[endNodeIdx];  // the 'tip' node for extension point
57099               var midNid = endNodeIdx === 0 ? way.nodes[1] : way.nodes[way.nodes.length - 2];  // the other node of the edge
57100               var tipNode = graph.entity(tipNid);
57101               var midNode = graph.entity(midNid);
57102               var lon = tipNode.loc[0];
57103               var lat = tipNode.loc[1];
57104               var lon_range = geoMetersToLon(EXTEND_TH_METERS, lat) / 2;
57105               var lat_range = geoMetersToLat(EXTEND_TH_METERS) / 2;
57106               var queryExtent = geoExtent([
57107                 [lon - lon_range, lat - lat_range],
57108                 [lon + lon_range, lat + lat_range]
57109               ]);
57110
57111               // first, extend the edge of [midNode -> tipNode] by EXTEND_TH_METERS and find the "extended tip" location
57112               var edgeLen = geoSphericalDistance(midNode.loc, tipNode.loc);
57113               var t = EXTEND_TH_METERS / edgeLen + 1.0;
57114               var extTipLoc = geoVecInterp(midNode.loc, tipNode.loc, t);
57115
57116               // then, check if the extension part [tipNode.loc -> extTipLoc] intersects any other ways
57117               var segmentInfos = tree.waySegments(queryExtent, graph);
57118               for (var i = 0; i < segmentInfos.length; i++) {
57119                 var segmentInfo = segmentInfos[i];
57120
57121                 var way2 = graph.entity(segmentInfo.wayId);
57122
57123                 if (!isHighway(way2)) { continue; }
57124
57125                 if (!canConnectWays(way, way2)) { continue; }
57126
57127                 var nAid = segmentInfo.nodes[0],
57128                   nBid = segmentInfo.nodes[1];
57129
57130                 if (nAid === tipNid || nBid === tipNid) { continue; }
57131
57132                 var nA = graph.entity(nAid),
57133                   nB = graph.entity(nBid);
57134                 var crossLoc = geoLineIntersection([tipNode.loc, extTipLoc], [nA.loc, nB.loc]);
57135                 if (crossLoc) {
57136                   return {
57137                     mid: midNode,
57138                     node: tipNode,
57139                     wid: way2.id,
57140                     edge: [nA.id, nB.id],
57141                     cross_loc: crossLoc
57142                   };
57143                 }
57144               }
57145               return null;
57146             }
57147           };
57148
57149           validation.type = type;
57150
57151           return validation;
57152         }
57153
57154         function validationCloseNodes(context) {
57155             var type = 'close_nodes';
57156
57157             var pointThresholdMeters = 0.2;
57158
57159             var validation = function(entity, graph) {
57160                 if (entity.type === 'node') {
57161                     return getIssuesForNode(entity);
57162                 } else if (entity.type === 'way') {
57163                     return getIssuesForWay(entity);
57164                 }
57165                 return [];
57166
57167                 function getIssuesForNode(node) {
57168                     var parentWays = graph.parentWays(node);
57169                     if (parentWays.length) {
57170                         return getIssuesForVertex(node, parentWays);
57171                     } else {
57172                         return getIssuesForDetachedPoint(node);
57173                     }
57174                 }
57175
57176                 function wayTypeFor(way) {
57177
57178                     if (way.tags.boundary && way.tags.boundary !== 'no') { return 'boundary'; }
57179                     if (way.tags.indoor && way.tags.indoor !== 'no') { return 'indoor'; }
57180                     if ((way.tags.building && way.tags.building !== 'no') ||
57181                         (way.tags['building:part'] && way.tags['building:part'] !== 'no')) { return 'building'; }
57182                     if (osmPathHighwayTagValues[way.tags.highway]) { return 'path'; }
57183
57184                     var parentRelations = graph.parentRelations(way);
57185                     for (var i in parentRelations) {
57186                         var relation = parentRelations[i];
57187
57188                         if (relation.tags.type === 'boundary') { return 'boundary'; }
57189
57190                         if (relation.isMultipolygon()) {
57191                             if (relation.tags.indoor && relation.tags.indoor !== 'no') { return 'indoor'; }
57192                             if ((relation.tags.building && relation.tags.building !== 'no') ||
57193                                 (relation.tags['building:part'] && relation.tags['building:part'] !== 'no')) { return 'building'; }
57194                         }
57195                     }
57196
57197                     return 'other';
57198                 }
57199
57200                 function shouldCheckWay(way) {
57201
57202                     // don't flag issues where merging would create degenerate ways
57203                     if (way.nodes.length <= 2 ||
57204                         (way.isClosed() && way.nodes.length <= 4)) { return false; }
57205
57206                     var bbox = way.extent(graph).bbox();
57207                     var hypotenuseMeters = geoSphericalDistance([bbox.minX, bbox.minY], [bbox.maxX, bbox.maxY]);
57208                     // don't flag close nodes in very small ways
57209                     if (hypotenuseMeters < 1.5) { return false; }
57210
57211                     return true;
57212                 }
57213
57214                 function getIssuesForWay(way) {
57215                     if (!shouldCheckWay(way)) { return []; }
57216
57217                     var issues = [],
57218                         nodes = graph.childNodes(way);
57219                     for (var i = 0; i < nodes.length - 1; i++) {
57220                         var node1 = nodes[i];
57221                         var node2 = nodes[i+1];
57222
57223                         var issue = getWayIssueIfAny(node1, node2, way);
57224                         if (issue) { issues.push(issue); }
57225                     }
57226                     return issues;
57227                 }
57228
57229                 function getIssuesForVertex(node, parentWays) {
57230                     var issues = [];
57231
57232                     function checkForCloseness(node1, node2, way) {
57233                         var issue = getWayIssueIfAny(node1, node2, way);
57234                         if (issue) { issues.push(issue); }
57235                     }
57236
57237                     for (var i = 0; i < parentWays.length; i++) {
57238                         var parentWay = parentWays[i];
57239
57240                         if (!shouldCheckWay(parentWay)) { continue; }
57241
57242                         var lastIndex = parentWay.nodes.length - 1;
57243                         for (var j = 0; j < parentWay.nodes.length; j++) {
57244                             if (j !== 0) {
57245                                 if (parentWay.nodes[j-1] === node.id) {
57246                                     checkForCloseness(node, graph.entity(parentWay.nodes[j]), parentWay);
57247                                 }
57248                             }
57249                             if (j !== lastIndex) {
57250                                 if (parentWay.nodes[j+1] === node.id) {
57251                                     checkForCloseness(graph.entity(parentWay.nodes[j]), node, parentWay);
57252                                 }
57253                             }
57254                         }
57255                     }
57256                     return issues;
57257                 }
57258
57259                 function thresholdMetersForWay(way) {
57260                     if (!shouldCheckWay(way)) { return 0; }
57261
57262                     var wayType = wayTypeFor(way);
57263
57264                     // don't flag boundaries since they might be highly detailed and can't be easily verified
57265                     if (wayType === 'boundary') { return 0; }
57266                     // expect some features to be mapped with higher levels of detail
57267                     if (wayType === 'indoor') { return 0.01; }
57268                     if (wayType === 'building') { return 0.05; }
57269                     if (wayType === 'path') { return 0.1; }
57270                     return 0.2;
57271                 }
57272
57273                 function getIssuesForDetachedPoint(node) {
57274
57275                     var issues = [];
57276
57277                     var lon = node.loc[0];
57278                     var lat = node.loc[1];
57279                     var lon_range = geoMetersToLon(pointThresholdMeters, lat) / 2;
57280                     var lat_range = geoMetersToLat(pointThresholdMeters) / 2;
57281                     var queryExtent = geoExtent([
57282                         [lon - lon_range, lat - lat_range],
57283                         [lon + lon_range, lat + lat_range]
57284                     ]);
57285
57286                     var intersected = context.history().tree().intersects(queryExtent, graph);
57287                     for (var j = 0; j < intersected.length; j++) {
57288                         var nearby = intersected[j];
57289
57290                         if (nearby.id === node.id) { continue; }
57291                         if (nearby.type !== 'node' || nearby.geometry(graph) !== 'point') { continue; }
57292
57293                         if (nearby.loc === node.loc ||
57294                             geoSphericalDistance(node.loc, nearby.loc) < pointThresholdMeters) {
57295
57296                             // allow very close points if tags indicate the z-axis might vary
57297                             var zAxisKeys = { layer: true, level: true, 'addr:housenumber': true, 'addr:unit': true };
57298                             var zAxisDifferentiates = false;
57299                             for (var key in zAxisKeys) {
57300                                 var nodeValue = node.tags[key] || '0';
57301                                 var nearbyValue = nearby.tags[key] || '0';
57302                                 if (nodeValue !== nearbyValue) {
57303                                     zAxisDifferentiates = true;
57304                                     break;
57305                                 }
57306                             }
57307                             if (zAxisDifferentiates) { continue; }
57308
57309                             issues.push(new validationIssue({
57310                                 type: type,
57311                                 subtype: 'detached',
57312                                 severity: 'warning',
57313                                 message: function(context) {
57314                                     var entity = context.hasEntity(this.entityIds[0]),
57315                                         entity2 = context.hasEntity(this.entityIds[1]);
57316                                     return (entity && entity2) ? _t('issues.close_nodes.detached.message', {
57317                                         feature: utilDisplayLabel(entity, context.graph()),
57318                                         feature2: utilDisplayLabel(entity2, context.graph())
57319                                     }) : '';
57320                                 },
57321                                 reference: showReference,
57322                                 entityIds: [node.id, nearby.id],
57323                                 dynamicFixes: function() {
57324                                     return [
57325                                         new validationIssueFix({
57326                                             icon: 'iD-operation-disconnect',
57327                                             title: _t('issues.fix.move_points_apart.title')
57328                                         }),
57329                                         new validationIssueFix({
57330                                             icon: 'iD-icon-layers',
57331                                             title: _t('issues.fix.use_different_layers_or_levels.title')
57332                                         })
57333                                     ];
57334                                 }
57335                             }));
57336                         }
57337                     }
57338
57339                     return issues;
57340
57341                     function showReference(selection) {
57342                         var referenceText = _t('issues.close_nodes.detached.reference');
57343                         selection.selectAll('.issue-reference')
57344                             .data([0])
57345                             .enter()
57346                             .append('div')
57347                             .attr('class', 'issue-reference')
57348                             .text(referenceText);
57349                     }
57350                 }
57351
57352                 function getWayIssueIfAny(node1, node2, way) {
57353                     if (node1.id === node2.id ||
57354                         (node1.hasInterestingTags() && node2.hasInterestingTags())) {
57355                         return null;
57356                     }
57357
57358                     if (node1.loc !== node2.loc) {
57359                         var parentWays1 = graph.parentWays(node1);
57360                         var parentWays2 = new Set(graph.parentWays(node2));
57361
57362                         var sharedWays = parentWays1.filter(function(parentWay) {
57363                             return parentWays2.has(parentWay);
57364                         });
57365
57366                         var thresholds = sharedWays.map(function(parentWay) {
57367                             return thresholdMetersForWay(parentWay);
57368                         });
57369
57370                         var threshold = Math.min.apply(Math, thresholds);
57371                         var distance = geoSphericalDistance(node1.loc, node2.loc);
57372                         if (distance > threshold) { return null; }
57373                     }
57374
57375                     return new validationIssue({
57376                         type: type,
57377                         subtype: 'vertices',
57378                         severity: 'warning',
57379                         message: function(context) {
57380                             var entity = context.hasEntity(this.entityIds[0]);
57381                             return entity ? _t('issues.close_nodes.message', { way: utilDisplayLabel(entity, context.graph()) }) : '';
57382                         },
57383                         reference: showReference,
57384                         entityIds: [way.id, node1.id, node2.id],
57385                         loc: node1.loc,
57386                         dynamicFixes: function() {
57387                             return [
57388                                 new validationIssueFix({
57389                                     icon: 'iD-icon-plus',
57390                                     title: _t('issues.fix.merge_points.title'),
57391                                     onClick: function(context) {
57392                                         var entityIds = this.issue.entityIds;
57393                                         var action = actionMergeNodes([entityIds[1], entityIds[2]]);
57394                                         context.perform(action, _t('issues.fix.merge_close_vertices.annotation'));
57395                                     }
57396                                 }),
57397                                 new validationIssueFix({
57398                                     icon: 'iD-operation-disconnect',
57399                                     title: _t('issues.fix.move_points_apart.title')
57400                                 })
57401                             ];
57402                         }
57403                     });
57404
57405                     function showReference(selection) {
57406                         var referenceText = _t('issues.close_nodes.reference');
57407                         selection.selectAll('.issue-reference')
57408                             .data([0])
57409                             .enter()
57410                             .append('div')
57411                             .attr('class', 'issue-reference')
57412                             .text(referenceText);
57413                     }
57414                 }
57415
57416             };
57417
57418
57419             validation.type = type;
57420
57421             return validation;
57422         }
57423
57424         function validationCrossingWays(context) {
57425             var type = 'crossing_ways';
57426
57427             // returns the way or its parent relation, whichever has a useful feature type
57428             function getFeatureWithFeatureTypeTagsForWay(way, graph) {
57429                 if (getFeatureType(way, graph) === null) {
57430                     // if the way doesn't match a feature type, check its parent relations
57431                     var parentRels = graph.parentRelations(way);
57432                     for (var i = 0; i < parentRels.length; i++) {
57433                         var rel = parentRels[i];
57434                         if (getFeatureType(rel, graph) !== null) {
57435                             return rel;
57436                         }
57437                     }
57438                 }
57439                 return way;
57440             }
57441
57442
57443             function hasTag(tags, key) {
57444                 return tags[key] !== undefined && tags[key] !== 'no';
57445             }
57446
57447             function taggedAsIndoor(tags) {
57448                 return hasTag(tags, 'indoor') ||
57449                     hasTag(tags, 'level') ||
57450                     tags.highway === 'corridor';
57451             }
57452
57453             function allowsBridge(featureType) {
57454                 return featureType === 'highway' || featureType === 'railway' || featureType === 'waterway';
57455             }
57456             function allowsTunnel(featureType) {
57457                 return featureType === 'highway' || featureType === 'railway' || featureType === 'waterway';
57458             }
57459
57460
57461             function getFeatureTypeForCrossingCheck(way, graph) {
57462                 var feature = getFeatureWithFeatureTypeTagsForWay(way, graph);
57463                 return getFeatureType(feature, graph);
57464             }
57465
57466             // discard
57467             var ignoredBuildings = {
57468                 demolished: true, dismantled: true, proposed: true, razed: true
57469             };
57470
57471
57472             function getFeatureType(entity, graph) {
57473
57474                 var geometry = entity.geometry(graph);
57475                 if (geometry !== 'line' && geometry !== 'area') { return null; }
57476
57477                 var tags = entity.tags;
57478
57479                 if (hasTag(tags, 'building') && !ignoredBuildings[tags.building]) { return 'building'; }
57480                 if (hasTag(tags, 'highway') && osmRoutableHighwayTagValues[tags.highway]) { return 'highway'; }
57481
57482                 // don't check railway or waterway areas
57483                 if (geometry !== 'line') { return null; }
57484
57485                 if (hasTag(tags, 'railway') && osmRailwayTrackTagValues[tags.railway]) { return 'railway'; }
57486                 if (hasTag(tags, 'waterway') && osmFlowingWaterwayTagValues[tags.waterway]) { return 'waterway'; }
57487
57488                 return null;
57489             }
57490
57491
57492             function isLegitCrossing(way1, featureType1, way2, featureType2) {
57493                 var tags1 = way1.tags;
57494                 var tags2 = way2.tags;
57495
57496                 // assume 0 by default
57497                 var level1 = tags1.level || '0';
57498                 var level2 = tags2.level || '0';
57499
57500                 if (taggedAsIndoor(tags1) && taggedAsIndoor(tags2) && level1 !== level2) {
57501                     // assume features don't interact if they're indoor on different levels
57502                     return true;
57503                 }
57504
57505                 // assume 0 by default; don't use way.layer() since we account for structures here
57506                 var layer1 = tags1.layer || '0';
57507                 var layer2 = tags2.layer || '0';
57508
57509                 if (allowsBridge(featureType1) && allowsBridge(featureType2)) {
57510                     if (hasTag(tags1, 'bridge') && !hasTag(tags2, 'bridge')) { return true; }
57511                     if (!hasTag(tags1, 'bridge') && hasTag(tags2, 'bridge')) { return true; }
57512                     // crossing bridges must use different layers
57513                     if (hasTag(tags1, 'bridge') && hasTag(tags2, 'bridge') && layer1 !== layer2) { return true; }
57514                 } else if (allowsBridge(featureType1) && hasTag(tags1, 'bridge')) { return true; }
57515                 else if (allowsBridge(featureType2) && hasTag(tags2, 'bridge')) { return true; }
57516
57517                 if (allowsTunnel(featureType1) && allowsTunnel(featureType2)) {
57518                     if (hasTag(tags1, 'tunnel') && !hasTag(tags2, 'tunnel')) { return true; }
57519                     if (!hasTag(tags1, 'tunnel') && hasTag(tags2, 'tunnel')) { return true; }
57520                     // crossing tunnels must use different layers
57521                     if (hasTag(tags1, 'tunnel') && hasTag(tags2, 'tunnel') && layer1 !== layer2) { return true; }
57522                 } else if (allowsTunnel(featureType1) && hasTag(tags1, 'tunnel')) { return true; }
57523                 else if (allowsTunnel(featureType2) && hasTag(tags2, 'tunnel')) { return true; }
57524
57525                 // don't flag crossing waterways and pier/highways
57526                 if (featureType1 === 'waterway' && featureType2 === 'highway' && tags2.man_made === 'pier') { return true; }
57527                 if (featureType2 === 'waterway' && featureType1 === 'highway' && tags1.man_made === 'pier') { return true; }
57528
57529                 if (featureType1 === 'building' || featureType2 === 'building') {
57530                     // for building crossings, different layers are enough
57531                     if (layer1 !== layer2) { return true; }
57532                 }
57533                 return false;
57534             }
57535
57536
57537             // highway values for which we shouldn't recommend connecting to waterways
57538             var highwaysDisallowingFords = {
57539                 motorway: true, motorway_link: true, trunk: true, trunk_link: true,
57540                 primary: true, primary_link: true, secondary: true, secondary_link: true
57541             };
57542             var nonCrossingHighways = { track: true };
57543
57544             function tagsForConnectionNodeIfAllowed(entity1, entity2, graph) {
57545                 var featureType1 = getFeatureType(entity1, graph);
57546                 var featureType2 = getFeatureType(entity2, graph);
57547
57548                 var geometry1 = entity1.geometry(graph);
57549                 var geometry2 = entity2.geometry(graph);
57550                 var bothLines = geometry1 === 'line' && geometry2 === 'line';
57551
57552                 if (featureType1 === featureType2) {
57553                     if (featureType1 === 'highway') {
57554                         var entity1IsPath = osmPathHighwayTagValues[entity1.tags.highway];
57555                         var entity2IsPath = osmPathHighwayTagValues[entity2.tags.highway];
57556                         if ((entity1IsPath || entity2IsPath) && entity1IsPath !== entity2IsPath) {
57557                             // one feature is a path but not both
57558
57559                             var roadFeature = entity1IsPath ? entity2 : entity1;
57560                             if (nonCrossingHighways[roadFeature.tags.highway]) {
57561                                 // don't mark path connections with certain roads as crossings
57562                                 return {};
57563                             }
57564                             var pathFeature = entity1IsPath ? entity1 : entity2;
57565                             if (['marked', 'unmarked'].indexOf(pathFeature.tags.crossing) !== -1) {
57566                                 // if the path is a crossing, match the crossing type
57567                                 return bothLines ? { highway: 'crossing', crossing: pathFeature.tags.crossing } : {};
57568                             }
57569                             // don't add a `crossing` subtag to ambiguous crossings
57570                             return bothLines ? { highway: 'crossing' } : {};
57571                         }
57572                         return {};
57573                     }
57574                     if (featureType1 === 'waterway') { return {}; }
57575                     if (featureType1 === 'railway') { return {}; }
57576
57577                 } else {
57578                     var featureTypes = [featureType1, featureType2];
57579                     if (featureTypes.indexOf('highway') !== -1) {
57580                         if (featureTypes.indexOf('railway') !== -1) {
57581                             if (osmPathHighwayTagValues[entity1.tags.highway] ||
57582                                 osmPathHighwayTagValues[entity2.tags.highway]) {
57583                                 // path-rail connections use this tag
57584                                 return bothLines ? { railway: 'crossing' } : {};
57585                             } else {
57586                                 // road-rail connections use this tag
57587                                 return bothLines ? { railway: 'level_crossing' } : {};
57588                             }
57589                         }
57590
57591                         if (featureTypes.indexOf('waterway') !== -1) {
57592                             // do not allow fords on structures
57593                             if (hasTag(entity1.tags, 'tunnel') && hasTag(entity2.tags, 'tunnel')) { return null; }
57594                             if (hasTag(entity1.tags, 'bridge') && hasTag(entity2.tags, 'bridge')) { return null; }
57595
57596                             if (highwaysDisallowingFords[entity1.tags.highway] ||
57597                                 highwaysDisallowingFords[entity2.tags.highway]) {
57598                                 // do not allow fords on major highways
57599                                 return null;
57600                             }
57601                             return bothLines ? { ford: 'yes' } : {};
57602                         }
57603                     }
57604                 }
57605                 return null;
57606             }
57607
57608
57609             function findCrossingsByWay(way1, graph, tree) {
57610                 var edgeCrossInfos = [];
57611                 if (way1.type !== 'way') { return edgeCrossInfos; }
57612
57613                 var way1FeatureType = getFeatureTypeForCrossingCheck(way1, graph);
57614                 if (way1FeatureType === null) { return edgeCrossInfos; }
57615
57616                 var checkedSingleCrossingWays = {};
57617
57618                 // declare vars ahead of time to reduce garbage collection
57619                 var i, j;
57620                 var extent;
57621                 var n1, n2, nA, nB, nAId, nBId;
57622                 var segment1, segment2;
57623                 var oneOnly;
57624                 var segmentInfos, segment2Info, way2, way2FeatureType;
57625                 var way1Nodes = graph.childNodes(way1);
57626                 var comparedWays = {};
57627                 for (i = 0; i < way1Nodes.length - 1; i++) {
57628                     n1 = way1Nodes[i];
57629                     n2 = way1Nodes[i + 1];
57630                     extent = geoExtent([
57631                         [
57632                             Math.min(n1.loc[0], n2.loc[0]),
57633                             Math.min(n1.loc[1], n2.loc[1])
57634                         ],
57635                         [
57636                             Math.max(n1.loc[0], n2.loc[0]),
57637                             Math.max(n1.loc[1], n2.loc[1])
57638                         ]
57639                     ]);
57640
57641                     // Optimize by only checking overlapping segments, not every segment
57642                     // of overlapping ways
57643                     segmentInfos = tree.waySegments(extent, graph);
57644
57645                     for (j = 0; j < segmentInfos.length; j++) {
57646                         segment2Info = segmentInfos[j];
57647
57648                         // don't check for self-intersection in this validation
57649                         if (segment2Info.wayId === way1.id) { continue; }
57650
57651                         // skip if this way was already checked and only one issue is needed
57652                         if (checkedSingleCrossingWays[segment2Info.wayId]) { continue; }
57653
57654                         // mark this way as checked even if there are no crossings
57655                         comparedWays[segment2Info.wayId] = true;
57656
57657                         way2 = graph.hasEntity(segment2Info.wayId);
57658                         if (!way2) { continue; }
57659
57660                         // only check crossing highway, waterway, building, and railway
57661                         way2FeatureType = getFeatureTypeForCrossingCheck(way2, graph);
57662                         if (way2FeatureType === null ||
57663                             isLegitCrossing(way1, way1FeatureType, way2, way2FeatureType)) {
57664                             continue;
57665                         }
57666
57667                         // create only one issue for building crossings
57668                         oneOnly = way1FeatureType === 'building' || way2FeatureType === 'building';
57669
57670                         nAId = segment2Info.nodes[0];
57671                         nBId = segment2Info.nodes[1];
57672                         if (nAId === n1.id || nAId === n2.id ||
57673                             nBId === n1.id || nBId === n2.id) {
57674                             // n1 or n2 is a connection node; skip
57675                             continue;
57676                         }
57677                         nA = graph.hasEntity(nAId);
57678                         if (!nA) { continue; }
57679                         nB = graph.hasEntity(nBId);
57680                         if (!nB) { continue; }
57681
57682                         segment1 = [n1.loc, n2.loc];
57683                         segment2 = [nA.loc, nB.loc];
57684                         var point = geoLineIntersection(segment1, segment2);
57685                         if (point) {
57686                             edgeCrossInfos.push({
57687                                 wayInfos: [
57688                                     {
57689                                         way: way1,
57690                                         featureType: way1FeatureType,
57691                                         edge: [n1.id, n2.id]
57692                                     },
57693                                     {
57694                                         way: way2,
57695                                         featureType: way2FeatureType,
57696                                         edge: [nA.id, nB.id]
57697                                     }
57698                                 ],
57699                                 crossPoint: point
57700                             });
57701                             if (oneOnly) {
57702                                 checkedSingleCrossingWays[way2.id] = true;
57703                                 break;
57704                             }
57705                         }
57706                     }
57707                 }
57708                 return edgeCrossInfos;
57709             }
57710
57711
57712             function waysToCheck(entity, graph) {
57713                 var featureType = getFeatureType(entity, graph);
57714                 if (!featureType) { return []; }
57715
57716                 if (entity.type === 'way') {
57717                     return [entity];
57718                 } else if (entity.type === 'relation') {
57719                     return entity.members.reduce(function(array, member) {
57720                         if (member.type === 'way' &&
57721                             // only look at geometry ways
57722                             (!member.role || member.role === 'outer' || member.role === 'inner')) {
57723                             var entity = graph.hasEntity(member.id);
57724                             // don't add duplicates
57725                             if (entity && array.indexOf(entity) === -1) {
57726                                 array.push(entity);
57727                             }
57728                         }
57729                         return array;
57730                     }, []);
57731                 }
57732                 return [];
57733             }
57734
57735
57736             var validation = function checkCrossingWays(entity, graph) {
57737
57738                 var tree = context.history().tree();
57739
57740                 var ways = waysToCheck(entity, graph);
57741
57742                 var issues = [];
57743                 // declare these here to reduce garbage collection
57744                 var wayIndex, crossingIndex, crossings;
57745                 for (wayIndex in ways) {
57746                     crossings = findCrossingsByWay(ways[wayIndex], graph, tree);
57747                     for (crossingIndex in crossings) {
57748                         issues.push(createIssue(crossings[crossingIndex], graph));
57749                     }
57750                 }
57751                 return issues;
57752             };
57753
57754
57755             function createIssue(crossing, graph) {
57756
57757                 // use the entities with the tags that define the feature type
57758                 crossing.wayInfos.sort(function(way1Info, way2Info) {
57759                     var type1 = way1Info.featureType;
57760                     var type2 = way2Info.featureType;
57761                     if (type1 === type2) {
57762                         return utilDisplayLabel(way1Info.way, graph) > utilDisplayLabel(way2Info.way, graph);
57763                     } else if (type1 === 'waterway') {
57764                         return true;
57765                     } else if (type2 === 'waterway') {
57766                         return false;
57767                     }
57768                     return type1 < type2;
57769                 });
57770                 var entities = crossing.wayInfos.map(function(wayInfo) {
57771                     return getFeatureWithFeatureTypeTagsForWay(wayInfo.way, graph);
57772                 });
57773                 var edges = [crossing.wayInfos[0].edge, crossing.wayInfos[1].edge];
57774                 var featureTypes = [crossing.wayInfos[0].featureType, crossing.wayInfos[1].featureType];
57775
57776                 var connectionTags = tagsForConnectionNodeIfAllowed(entities[0], entities[1], graph);
57777
57778                 var featureType1 = crossing.wayInfos[0].featureType;
57779                 var featureType2 = crossing.wayInfos[1].featureType;
57780
57781                 var isCrossingIndoors = taggedAsIndoor(entities[0].tags) && taggedAsIndoor(entities[1].tags);
57782                 var isCrossingTunnels = allowsTunnel(featureType1) && hasTag(entities[0].tags, 'tunnel') &&
57783                                         allowsTunnel(featureType2) && hasTag(entities[1].tags, 'tunnel');
57784                 var isCrossingBridges = allowsBridge(featureType1) && hasTag(entities[0].tags, 'bridge') &&
57785                                         allowsBridge(featureType2) && hasTag(entities[1].tags, 'bridge');
57786
57787                 var subtype = [featureType1, featureType2].sort().join('-');
57788
57789                 var crossingTypeID = subtype;
57790
57791                 if (isCrossingIndoors) {
57792                     crossingTypeID = 'indoor-indoor';
57793                 } else if (isCrossingTunnels) {
57794                     crossingTypeID = 'tunnel-tunnel';
57795                 } else if (isCrossingBridges) {
57796                     crossingTypeID = 'bridge-bridge';
57797                 }
57798                 if (connectionTags && (isCrossingIndoors || isCrossingTunnels || isCrossingBridges)) {
57799                     crossingTypeID += '_connectable';
57800                 }
57801
57802                 return new validationIssue({
57803                     type: type,
57804                     subtype: subtype,
57805                     severity: 'warning',
57806                     message: function(context) {
57807                         var graph = context.graph();
57808                         var entity1 = graph.hasEntity(this.entityIds[0]),
57809                             entity2 = graph.hasEntity(this.entityIds[1]);
57810                         return (entity1 && entity2) ? _t('issues.crossing_ways.message', {
57811                             feature: utilDisplayLabel(entity1, graph),
57812                             feature2: utilDisplayLabel(entity2, graph)
57813                         }) : '';
57814                     },
57815                     reference: showReference,
57816                     entityIds: entities.map(function(entity) {
57817                         return entity.id;
57818                     }),
57819                     data: {
57820                         edges: edges,
57821                         featureTypes: featureTypes,
57822                         connectionTags: connectionTags
57823                     },
57824                     // differentiate based on the loc since two ways can cross multiple times
57825                     hash: crossing.crossPoint.toString() +
57826                         // if the edges change then so does the fix
57827                         edges.slice().sort(function(edge1, edge2) {
57828                             // order to assure hash is deterministic
57829                             return edge1[0] < edge2[0] ? -1 : 1;
57830                         }).toString() +
57831                         // ensure the correct connection tags are added in the fix
57832                         JSON.stringify(connectionTags),
57833                     loc: crossing.crossPoint,
57834                     dynamicFixes: function(context) {
57835                         var mode = context.mode();
57836                         if (!mode || mode.id !== 'select' || mode.selectedIDs().length !== 1) { return []; }
57837
57838                         var selectedIndex = this.entityIds[0] === mode.selectedIDs()[0] ? 0 : 1;
57839                         var selectedFeatureType = this.data.featureTypes[selectedIndex];
57840                         var otherFeatureType = this.data.featureTypes[selectedIndex === 0 ? 1 : 0];
57841
57842                         var fixes = [];
57843
57844                         if (connectionTags) {
57845                             fixes.push(makeConnectWaysFix(this.data.connectionTags));
57846                         }
57847
57848                         if (isCrossingIndoors) {
57849                             fixes.push(new validationIssueFix({
57850                                 icon: 'iD-icon-layers',
57851                                 title: _t('issues.fix.use_different_levels.title')
57852                             }));
57853                         } else if (isCrossingTunnels ||
57854                             isCrossingBridges ||
57855                             featureType1 === 'building' ||
57856                             featureType2 === 'building')  {
57857
57858                             fixes.push(makeChangeLayerFix('higher'));
57859                             fixes.push(makeChangeLayerFix('lower'));
57860
57861                         // can only add bridge/tunnel if both features are lines
57862                         } else if (context.graph().geometry(this.entityIds[0]) === 'line' &&
57863                             context.graph().geometry(this.entityIds[1]) === 'line') {
57864
57865                             // don't recommend adding bridges to waterways since they're uncommmon
57866                             if (allowsBridge(selectedFeatureType) && selectedFeatureType !== 'waterway') {
57867                                 fixes.push(makeAddBridgeOrTunnelFix('add_a_bridge', 'temaki-bridge', 'bridge'));
57868                             }
57869
57870                             // don't recommend adding tunnels under waterways since they're uncommmon
57871                             var skipTunnelFix = otherFeatureType === 'waterway' && selectedFeatureType !== 'waterway';
57872                             if (allowsTunnel(selectedFeatureType) && !skipTunnelFix) {
57873                                 fixes.push(makeAddBridgeOrTunnelFix('add_a_tunnel', 'temaki-tunnel', 'tunnel'));
57874                             }
57875                         }
57876
57877                         // repositioning the features is always an option
57878                         fixes.push(new validationIssueFix({
57879                             icon: 'iD-operation-move',
57880                             title: _t('issues.fix.reposition_features.title')
57881                         }));
57882
57883                         return fixes;
57884                     }
57885                 });
57886
57887                 function showReference(selection) {
57888                     selection.selectAll('.issue-reference')
57889                         .data([0])
57890                         .enter()
57891                         .append('div')
57892                         .attr('class', 'issue-reference')
57893                         .text(_t('issues.crossing_ways.' + crossingTypeID + '.reference'));
57894                 }
57895             }
57896
57897             function makeAddBridgeOrTunnelFix(fixTitleID, iconName, bridgeOrTunnel){
57898                 return new validationIssueFix({
57899                     icon: iconName,
57900                     title: _t('issues.fix.' + fixTitleID + '.title'),
57901                     onClick: function(context) {
57902                         var mode = context.mode();
57903                         if (!mode || mode.id !== 'select') { return; }
57904
57905                         var selectedIDs = mode.selectedIDs();
57906                         if (selectedIDs.length !== 1) { return; }
57907
57908                         var selectedWayID = selectedIDs[0];
57909                         if (!context.hasEntity(selectedWayID)) { return; }
57910
57911                         var resultWayIDs = [selectedWayID];
57912
57913                         var edge, crossedEdge, crossedWayID;
57914                         if (this.issue.entityIds[0] === selectedWayID) {
57915                             edge = this.issue.data.edges[0];
57916                             crossedEdge = this.issue.data.edges[1];
57917                             crossedWayID = this.issue.entityIds[1];
57918                         } else {
57919                             edge = this.issue.data.edges[1];
57920                             crossedEdge = this.issue.data.edges[0];
57921                             crossedWayID = this.issue.entityIds[0];
57922                         }
57923
57924                         var crossingLoc = this.issue.loc;
57925
57926                         var projection = context.projection;
57927
57928                         var action = function actionAddStructure(graph) {
57929
57930                             var edgeNodes = [graph.entity(edge[0]), graph.entity(edge[1])];
57931
57932                             var crossedWay = graph.hasEntity(crossedWayID);
57933                             // use the explicit width of the crossed feature as the structure length, if available
57934                             var structLengthMeters = crossedWay && crossedWay.tags.width && parseFloat(crossedWay.tags.width);
57935                             if (!structLengthMeters) {
57936                                 // if no explicit width is set, approximate the width based on the tags
57937                                 structLengthMeters = crossedWay && crossedWay.impliedLineWidthMeters();
57938                             }
57939                             if (structLengthMeters) {
57940                                 if (getFeatureType(crossedWay, graph) === 'railway') {
57941                                     // bridges over railways are generally much longer than the rail bed itself, compensate
57942                                     structLengthMeters *= 2;
57943                                 }
57944                             } else {
57945                                 // should ideally never land here since all rail/water/road tags should have an implied width
57946                                 structLengthMeters = 8;
57947                             }
57948
57949                             var a1 = geoAngle(edgeNodes[0], edgeNodes[1], projection) + Math.PI;
57950                             var a2 = geoAngle(graph.entity(crossedEdge[0]), graph.entity(crossedEdge[1]), projection) + Math.PI;
57951                             var crossingAngle = Math.max(a1, a2) - Math.min(a1, a2);
57952                             if (crossingAngle > Math.PI) { crossingAngle -= Math.PI; }
57953                             // lengthen the structure to account for the angle of the crossing
57954                             structLengthMeters = ((structLengthMeters / 2) / Math.sin(crossingAngle)) * 2;
57955
57956                             // add padding since the structure must extend past the edges of the crossed feature
57957                             structLengthMeters += 4;
57958
57959                             // clamp the length to a reasonable range
57960                             structLengthMeters = Math.min(Math.max(structLengthMeters, 4), 50);
57961
57962                             function geomToProj(geoPoint) {
57963                                 return [
57964                                     geoLonToMeters(geoPoint[0], geoPoint[1]),
57965                                     geoLatToMeters(geoPoint[1])
57966                                 ];
57967                             }
57968                             function projToGeom(projPoint) {
57969                                 var lat = geoMetersToLat(projPoint[1]);
57970                                 return [
57971                                     geoMetersToLon(projPoint[0], lat),
57972                                     lat
57973                                 ];
57974                             }
57975
57976                             var projEdgeNode1 = geomToProj(edgeNodes[0].loc);
57977                             var projEdgeNode2 = geomToProj(edgeNodes[1].loc);
57978
57979                             var projectedAngle = geoVecAngle(projEdgeNode1, projEdgeNode2);
57980
57981                             var projectedCrossingLoc = geomToProj(crossingLoc);
57982                             var linearToSphericalMetersRatio = geoVecLength(projEdgeNode1, projEdgeNode2) /
57983                                 geoSphericalDistance(edgeNodes[0].loc, edgeNodes[1].loc);
57984
57985                             function locSphericalDistanceFromCrossingLoc(angle, distanceMeters) {
57986                                 var lengthSphericalMeters = distanceMeters * linearToSphericalMetersRatio;
57987                                 return projToGeom([
57988                                     projectedCrossingLoc[0] + Math.cos(angle) * lengthSphericalMeters,
57989                                     projectedCrossingLoc[1] + Math.sin(angle) * lengthSphericalMeters
57990                                 ]);
57991                             }
57992
57993                             var endpointLocGetter1 = function(lengthMeters) {
57994                                 return locSphericalDistanceFromCrossingLoc(projectedAngle, lengthMeters);
57995                             };
57996                             var endpointLocGetter2 = function(lengthMeters) {
57997                                 return locSphericalDistanceFromCrossingLoc(projectedAngle + Math.PI, lengthMeters);
57998                             };
57999
58000                             // avoid creating very short edges from splitting too close to another node
58001                             var minEdgeLengthMeters = 0.55;
58002
58003                             // decide where to bound the structure along the way, splitting as necessary
58004                             function determineEndpoint(edge, endNode, locGetter) {
58005                                 var newNode;
58006
58007                                 var idealLengthMeters = structLengthMeters / 2;
58008
58009                                 // distance between the crossing location and the end of the edge,
58010                                 // the maximum length of this side of the structure
58011                                 var crossingToEdgeEndDistance = geoSphericalDistance(crossingLoc, endNode.loc);
58012
58013                                 if (crossingToEdgeEndDistance - idealLengthMeters > minEdgeLengthMeters) {
58014                                     // the edge is long enough to insert a new node
58015
58016                                     // the loc that would result in the full expected length
58017                                     var idealNodeLoc = locGetter(idealLengthMeters);
58018
58019                                     newNode = osmNode();
58020                                     graph = actionAddMidpoint({ loc: idealNodeLoc, edge: edge }, newNode)(graph);
58021
58022                                 } else {
58023                                     var edgeCount = 0;
58024                                     endNode.parentIntersectionWays(graph).forEach(function(way) {
58025                                         way.nodes.forEach(function(nodeID) {
58026                                             if (nodeID === endNode.id) {
58027                                                 if ((endNode.id === way.first() && endNode.id !== way.last()) ||
58028                                                     (endNode.id === way.last() && endNode.id !== way.first())) {
58029                                                     edgeCount += 1;
58030                                                 } else {
58031                                                     edgeCount += 2;
58032                                                 }
58033                                             }
58034                                         });
58035                                     });
58036
58037                                     if (edgeCount >= 3) {
58038                                         // the end node is a junction, try to leave a segment
58039                                         // between it and the structure - #7202
58040
58041                                         var insetLength = crossingToEdgeEndDistance - minEdgeLengthMeters;
58042                                         if (insetLength > minEdgeLengthMeters) {
58043                                             var insetNodeLoc = locGetter(insetLength);
58044                                             newNode = osmNode();
58045                                             graph = actionAddMidpoint({ loc: insetNodeLoc, edge: edge }, newNode)(graph);
58046                                         }
58047                                     }
58048                                 }
58049
58050                                 // if the edge is too short to subdivide as desired, then
58051                                 // just bound the structure at the existing end node
58052                                 if (!newNode) { newNode = endNode; }
58053
58054                                 var splitAction = actionSplit(newNode.id)
58055                                     .limitWays(resultWayIDs); // only split selected or created ways
58056
58057                                 // do the split
58058                                 graph = splitAction(graph);
58059                                 if (splitAction.getCreatedWayIDs().length) {
58060                                     resultWayIDs.push(splitAction.getCreatedWayIDs()[0]);
58061                                 }
58062
58063                                 return newNode;
58064                             }
58065
58066                             var structEndNode1 = determineEndpoint(edge, edgeNodes[1], endpointLocGetter1);
58067                             var structEndNode2 = determineEndpoint([edgeNodes[0].id, structEndNode1.id], edgeNodes[0], endpointLocGetter2);
58068
58069                             var structureWay = resultWayIDs.map(function(id) {
58070                                 return graph.entity(id);
58071                             }).find(function(way) {
58072                                 return way.nodes.indexOf(structEndNode1.id) !== -1 &&
58073                                     way.nodes.indexOf(structEndNode2.id) !== -1;
58074                             });
58075
58076                             var tags = Object.assign({}, structureWay.tags); // copy tags
58077                             if (bridgeOrTunnel === 'bridge'){
58078                                 tags.bridge = 'yes';
58079                                 tags.layer = '1';
58080                             } else {
58081                                 var tunnelValue = 'yes';
58082                                 if (getFeatureType(structureWay, graph) === 'waterway') {
58083                                     // use `tunnel=culvert` for waterways by default
58084                                     tunnelValue = 'culvert';
58085                                 }
58086                                 tags.tunnel = tunnelValue;
58087                                 tags.layer = '-1';
58088                             }
58089                             // apply the structure tags to the way
58090                             graph = actionChangeTags(structureWay.id, tags)(graph);
58091                             return graph;
58092                         };
58093
58094                         context.perform(action, _t('issues.fix.' + fixTitleID + '.annotation'));
58095                         context.enter(modeSelect(context, resultWayIDs));
58096                     }
58097                 });
58098             }
58099
58100             function makeConnectWaysFix(connectionTags) {
58101
58102                 var fixTitleID = 'connect_features';
58103                 if (connectionTags.ford) {
58104                     fixTitleID = 'connect_using_ford';
58105                 }
58106
58107                 return new validationIssueFix({
58108                     icon: 'iD-icon-crossing',
58109                     title: _t('issues.fix.' + fixTitleID + '.title'),
58110                     onClick: function(context) {
58111                         var loc = this.issue.loc;
58112                         var connectionTags = this.issue.data.connectionTags;
58113                         var edges = this.issue.data.edges;
58114
58115                         context.perform(
58116                             function actionConnectCrossingWays(graph) {
58117                                 // create the new node for the points
58118                                 var node = osmNode({ loc: loc, tags: connectionTags });
58119                                 graph = graph.replace(node);
58120
58121                                 var nodesToMerge = [node.id];
58122                                 var mergeThresholdInMeters = 0.75;
58123
58124                                 edges.forEach(function(edge) {
58125                                     var edgeNodes = [graph.entity(edge[0]), graph.entity(edge[1])];
58126                                     var closestNodeInfo = geoSphericalClosestNode(edgeNodes, loc);
58127                                     // if there is already a point nearby, use that
58128                                     if (closestNodeInfo.distance < mergeThresholdInMeters) {
58129                                         nodesToMerge.push(closestNodeInfo.node.id);
58130                                     // else add the new node to the way
58131                                     } else {
58132                                         graph = actionAddMidpoint({loc: loc, edge: edge}, node)(graph);
58133                                     }
58134                                 });
58135
58136                                 if (nodesToMerge.length > 1) {
58137                                     // if we're using nearby nodes, merge them with the new node
58138                                     graph = actionMergeNodes(nodesToMerge, loc)(graph);
58139                                 }
58140
58141                                 return graph;
58142                             },
58143                             _t('issues.fix.connect_crossing_features.annotation')
58144                         );
58145                     }
58146                 });
58147             }
58148
58149             function makeChangeLayerFix(higherOrLower) {
58150                 return new validationIssueFix({
58151                     icon: 'iD-icon-' + (higherOrLower === 'higher' ? 'up' : 'down'),
58152                     title: _t('issues.fix.tag_this_as_' + higherOrLower + '.title'),
58153                     onClick: function(context) {
58154
58155                         var mode = context.mode();
58156                         if (!mode || mode.id !== 'select') { return; }
58157
58158                         var selectedIDs = mode.selectedIDs();
58159                         if (selectedIDs.length !== 1) { return; }
58160
58161                         var selectedID = selectedIDs[0];
58162                         if (!this.issue.entityIds.some(function(entityId) {
58163                             return entityId === selectedID;
58164                         })) { return; }
58165
58166                         var entity = context.hasEntity(selectedID);
58167                         if (!entity) { return; }
58168
58169                         var tags = Object.assign({}, entity.tags);   // shallow copy
58170                         var layer = tags.layer && Number(tags.layer);
58171                         if (layer && !isNaN(layer)) {
58172                             if (higherOrLower === 'higher') {
58173                                 layer += 1;
58174                             } else {
58175                                 layer -= 1;
58176                             }
58177                         } else {
58178                             if (higherOrLower === 'higher') {
58179                                 layer = 1;
58180                             } else {
58181                                 layer = -1;
58182                             }
58183                         }
58184                         tags.layer = layer.toString();
58185                         context.perform(
58186                             actionChangeTags(entity.id, tags),
58187                             _t('operations.change_tags.annotation')
58188                         );
58189                     }
58190                 });
58191             }
58192
58193             validation.type = type;
58194
58195             return validation;
58196         }
58197
58198         function validationDisconnectedWay() {
58199             var type = 'disconnected_way';
58200
58201             function isTaggedAsHighway(entity) {
58202                 return osmRoutableHighwayTagValues[entity.tags.highway];
58203             }
58204
58205             var validation = function checkDisconnectedWay(entity, graph) {
58206
58207                 var routingIslandWays = routingIslandForEntity(entity);
58208                 if (!routingIslandWays) { return []; }
58209
58210                 return [new validationIssue({
58211                     type: type,
58212                     subtype: 'highway',
58213                     severity: 'warning',
58214                     message: function(context) {
58215                         if (this.entityIds.length === 1) {
58216                             var entity = context.hasEntity(this.entityIds[0]);
58217                             return entity ? _t('issues.disconnected_way.highway.message', { highway: utilDisplayLabel(entity, context.graph()) }) : '';
58218                         }
58219                         return _t('issues.disconnected_way.routable.message.multiple', { count: this.entityIds.length.toString() });
58220                     },
58221                     reference: showReference,
58222                     entityIds: Array.from(routingIslandWays).map(function(way) { return way.id; }),
58223                     dynamicFixes: makeFixes
58224                 })];
58225
58226
58227                 function makeFixes(context) {
58228
58229                     var fixes = [];
58230
58231                     var singleEntity = this.entityIds.length === 1 && context.hasEntity(this.entityIds[0]);
58232
58233                     if (singleEntity) {
58234
58235                         if (singleEntity.type === 'way' && !singleEntity.isClosed()) {
58236
58237                             var textDirection = _mainLocalizer.textDirection();
58238
58239                             var startFix = makeContinueDrawingFixIfAllowed(textDirection, singleEntity.first(), 'start');
58240                             if (startFix) { fixes.push(startFix); }
58241
58242                             var endFix = makeContinueDrawingFixIfAllowed(textDirection, singleEntity.last(), 'end');
58243                             if (endFix) { fixes.push(endFix); }
58244                         }
58245                         if (!fixes.length) {
58246                             fixes.push(new validationIssueFix({
58247                                 title: _t('issues.fix.connect_feature.title')
58248                             }));
58249                         }
58250
58251                         fixes.push(new validationIssueFix({
58252                             icon: 'iD-operation-delete',
58253                             title: _t('issues.fix.delete_feature.title'),
58254                             entityIds: [singleEntity.id],
58255                             onClick: function(context) {
58256                                 var id = this.issue.entityIds[0];
58257                                 var operation = operationDelete(context, [id]);
58258                                 if (!operation.disabled()) {
58259                                     operation();
58260                                 }
58261                             }
58262                         }));
58263                     } else {
58264                         fixes.push(new validationIssueFix({
58265                             title: _t('issues.fix.connect_features.title')
58266                         }));
58267                     }
58268
58269                     return fixes;
58270                 }
58271
58272
58273                 function showReference(selection) {
58274                     selection.selectAll('.issue-reference')
58275                         .data([0])
58276                         .enter()
58277                         .append('div')
58278                         .attr('class', 'issue-reference')
58279                         .text(_t('issues.disconnected_way.routable.reference'));
58280                 }
58281
58282                 function routingIslandForEntity(entity) {
58283
58284                     var routingIsland = new Set();  // the interconnected routable features
58285                     var waysToCheck = [];           // the queue of remaining routable ways to traverse
58286
58287                     function queueParentWays(node) {
58288                         graph.parentWays(node).forEach(function(parentWay) {
58289                             if (!routingIsland.has(parentWay) &&    // only check each feature once
58290                                 isRoutableWay(parentWay, false)) {  // only check routable features
58291                                 routingIsland.add(parentWay);
58292                                 waysToCheck.push(parentWay);
58293                             }
58294                         });
58295                     }
58296
58297                     if (entity.type === 'way' && isRoutableWay(entity, true)) {
58298
58299                         routingIsland.add(entity);
58300                         waysToCheck.push(entity);
58301
58302                     } else if (entity.type === 'node' && isRoutableNode(entity)) {
58303
58304                         routingIsland.add(entity);
58305                         queueParentWays(entity);
58306
58307                     } else {
58308                         // this feature isn't routable, cannot be a routing island
58309                         return null;
58310                     }
58311
58312                     while (waysToCheck.length) {
58313                         var wayToCheck = waysToCheck.pop();
58314                         var childNodes = graph.childNodes(wayToCheck);
58315                         for (var i in childNodes) {
58316                             var vertex = childNodes[i];
58317
58318                             if (isConnectedVertex(vertex)) {
58319                                 // found a link to the wider network, not a routing island
58320                                 return null;
58321                             }
58322
58323                             if (isRoutableNode(vertex)) {
58324                                 routingIsland.add(vertex);
58325                             }
58326
58327                             queueParentWays(vertex);
58328                         }
58329                     }
58330
58331                     // no network link found, this is a routing island, return its members
58332                     return routingIsland;
58333                 }
58334
58335                 function isConnectedVertex(vertex) {
58336                     // assume ways overlapping unloaded tiles are connected to the wider road network  - #5938
58337                     var osm = services.osm;
58338                     if (osm && !osm.isDataLoaded(vertex.loc)) { return true; }
58339
58340                     // entrances are considered connected
58341                     if (vertex.tags.entrance &&
58342                         vertex.tags.entrance !== 'no') { return true; }
58343                     if (vertex.tags.amenity === 'parking_entrance') { return true; }
58344
58345                     return false;
58346                 }
58347
58348                 function isRoutableNode(node) {
58349                     // treat elevators as distinct features in the highway network
58350                     if (node.tags.highway === 'elevator') { return true; }
58351                     return false;
58352                 }
58353
58354                 function isRoutableWay(way, ignoreInnerWays) {
58355                     if (isTaggedAsHighway(way) || way.tags.route === 'ferry') { return true; }
58356
58357                     return graph.parentRelations(way).some(function(parentRelation) {
58358                         if (parentRelation.tags.type === 'route' &&
58359                             parentRelation.tags.route === 'ferry') { return true; }
58360
58361                         if (parentRelation.isMultipolygon() &&
58362                             isTaggedAsHighway(parentRelation) &&
58363                             (!ignoreInnerWays || parentRelation.memberById(way.id).role !== 'inner')) { return true; }
58364                     });
58365                 }
58366
58367                 function makeContinueDrawingFixIfAllowed(textDirection, vertexID, whichEnd) {
58368                     var vertex = graph.hasEntity(vertexID);
58369                     if (!vertex || vertex.tags.noexit === 'yes') { return null; }
58370
58371                     var useLeftContinue = (whichEnd === 'start' && textDirection === 'ltr') ||
58372                         (whichEnd === 'end' && textDirection === 'rtl');
58373
58374                     return new validationIssueFix({
58375                         icon: 'iD-operation-continue' + (useLeftContinue ? '-left' : ''),
58376                         title: _t('issues.fix.continue_from_' + whichEnd + '.title'),
58377                         entityIds: [vertexID],
58378                         onClick: function(context) {
58379                             var wayId = this.issue.entityIds[0];
58380                             var way = context.hasEntity(wayId);
58381                             var vertexId = this.entityIds[0];
58382                             var vertex = context.hasEntity(vertexId);
58383
58384                             if (!way || !vertex) { return; }
58385
58386                             // make sure the vertex is actually visible and editable
58387                             var map = context.map();
58388                             if (!context.editable() || !map.trimmedExtent().contains(vertex.loc)) {
58389                                 map.zoomToEase(vertex);
58390                             }
58391
58392                             context.enter(
58393                                 modeDrawLine(context, wayId, context.graph(), 'line', way.affix(vertexId), true)
58394                             );
58395                         }
58396                     });
58397                 }
58398
58399             };
58400
58401             validation.type = type;
58402
58403             return validation;
58404         }
58405
58406         function validationFormatting() {
58407             var type = 'invalid_format';
58408
58409             var validation = function(entity) {
58410                 var issues = [];
58411
58412                 function isValidEmail(email) {
58413                     // Emails in OSM are going to be official so they should be pretty simple
58414                     // Using negated lists to better support all possible unicode characters (#6494)
58415                     var valid_email = /^[^\(\)\\,":;<>@\[\]]+@[^\(\)\\,":;<>@\[\]\.]+(?:\.[a-z0-9-]+)*$/i;
58416
58417                     // An empty value is also acceptable
58418                     return (!email || valid_email.test(email));
58419                 }
58420                 /*
58421                 function isSchemePresent(url) {
58422                     var valid_scheme = /^https?:\/\//i;
58423                     return (!url || valid_scheme.test(url));
58424                 }
58425                 */
58426                 function showReferenceEmail(selection) {
58427                     selection.selectAll('.issue-reference')
58428                         .data([0])
58429                         .enter()
58430                         .append('div')
58431                         .attr('class', 'issue-reference')
58432                         .text(_t('issues.invalid_format.email.reference'));
58433                 }
58434                 /*
58435                 function showReferenceWebsite(selection) {
58436                     selection.selectAll('.issue-reference')
58437                         .data([0])
58438                         .enter()
58439                         .append('div')
58440                         .attr('class', 'issue-reference')
58441                         .text(t('issues.invalid_format.website.reference'));
58442                 }
58443
58444                 if (entity.tags.website) {
58445                     // Multiple websites are possible
58446                     // If ever we support ES6, arrow functions make this nicer
58447                     var websites = entity.tags.website
58448                         .split(';')
58449                         .map(function(s) { return s.trim(); })
58450                         .filter(function(x) { return !isSchemePresent(x); });
58451
58452                     if (websites.length) {
58453                         issues.push(new validationIssue({
58454                             type: type,
58455                             subtype: 'website',
58456                             severity: 'warning',
58457                             message: function(context) {
58458                                 var entity = context.hasEntity(this.entityIds[0]);
58459                                 return entity ? t('issues.invalid_format.website.message' + this.data,
58460                                     { feature: utilDisplayLabel(entity, context.graph()), site: websites.join(', ') }) : '';
58461                             },
58462                             reference: showReferenceWebsite,
58463                             entityIds: [entity.id],
58464                             hash: websites.join(),
58465                             data: (websites.length > 1) ? '_multi' : ''
58466                         }));
58467                     }
58468                 }
58469                 */
58470                 if (entity.tags.email) {
58471                     // Multiple emails are possible
58472                     var emails = entity.tags.email
58473                         .split(';')
58474                         .map(function(s) { return s.trim(); })
58475                         .filter(function(x) { return !isValidEmail(x); });
58476
58477                     if (emails.length) {
58478                         issues.push(new validationIssue({
58479                             type: type,
58480                             subtype: 'email',
58481                             severity: 'warning',
58482                             message: function(context) {
58483                                 var entity = context.hasEntity(this.entityIds[0]);
58484                                 return entity ? _t('issues.invalid_format.email.message' + this.data,
58485                                     { feature: utilDisplayLabel(entity, context.graph()), email: emails.join(', ') }) : '';
58486                             },
58487                             reference: showReferenceEmail,
58488                             entityIds: [entity.id],
58489                             hash: emails.join(),
58490                             data: (emails.length > 1) ? '_multi' : ''
58491                         }));
58492                     }
58493                 }
58494
58495                 return issues;
58496             };
58497
58498             validation.type = type;
58499
58500             return validation;
58501         }
58502
58503         function validationHelpRequest(context) {
58504             var type = 'help_request';
58505
58506             var validation = function checkFixmeTag(entity) {
58507
58508                 if (!entity.tags.fixme) { return []; }
58509
58510                 // don't flag fixmes on features added by the user
58511                 if (entity.version === undefined) { return []; }
58512
58513                 if (entity.v !== undefined) {
58514                     var baseEntity = context.history().base().hasEntity(entity.id);
58515                     // don't flag fixmes added by the user on existing features
58516                     if (!baseEntity || !baseEntity.tags.fixme) { return []; }
58517                 }
58518
58519                 return [new validationIssue({
58520                     type: type,
58521                     subtype: 'fixme_tag',
58522                     severity: 'warning',
58523                     message: function(context) {
58524                         var entity = context.hasEntity(this.entityIds[0]);
58525                         return entity ? _t('issues.fixme_tag.message', { feature: utilDisplayLabel(entity, context.graph()) }) : '';
58526                     },
58527                     dynamicFixes: function() {
58528                         return [
58529                             new validationIssueFix({
58530                                 title: _t('issues.fix.address_the_concern.title')
58531                             })
58532                         ];
58533                     },
58534                     reference: showReference,
58535                     entityIds: [entity.id]
58536                 })];
58537
58538                 function showReference(selection) {
58539                     selection.selectAll('.issue-reference')
58540                         .data([0])
58541                         .enter()
58542                         .append('div')
58543                         .attr('class', 'issue-reference')
58544                         .text(_t('issues.fixme_tag.reference'));
58545                 }
58546             };
58547
58548             validation.type = type;
58549
58550             return validation;
58551         }
58552
58553         function validationImpossibleOneway() {
58554             var type = 'impossible_oneway';
58555
58556             var validation = function checkImpossibleOneway(entity, graph) {
58557
58558                 if (entity.type !== 'way' || entity.geometry(graph) !== 'line') { return []; }
58559
58560                 if (entity.isClosed()) { return []; }
58561
58562                 if (!typeForWay(entity)) { return []; }
58563
58564                 if (!isOneway(entity)) { return []; }
58565
58566                 var firstIssues = issuesForNode(entity, entity.first());
58567                 var lastIssues = issuesForNode(entity, entity.last());
58568
58569                 return firstIssues.concat(lastIssues);
58570
58571                 function typeForWay(way) {
58572                     if (way.geometry(graph) !== 'line') { return null; }
58573
58574                     if (osmRoutableHighwayTagValues[way.tags.highway]) { return 'highway'; }
58575                     if (osmFlowingWaterwayTagValues[way.tags.waterway]) { return 'waterway'; }
58576                     return null;
58577                 }
58578
58579                 function isOneway(way) {
58580                     if (way.tags.oneway === 'yes') { return true; }
58581                     if (way.tags.oneway) { return false; }
58582
58583                     for (var key in way.tags) {
58584                         if (osmOneWayTags[key] && osmOneWayTags[key][way.tags[key]]) {
58585                             return true;
58586                         }
58587                     }
58588                     return false;
58589                 }
58590
58591                 function nodeOccursMoreThanOnce(way, nodeID) {
58592                     var occurences = 0;
58593                     for (var index in way.nodes) {
58594                         if (way.nodes[index] === nodeID) {
58595                             occurences += 1;
58596                             if (occurences > 1) { return true; }
58597                         }
58598                     }
58599                     return false;
58600                 }
58601
58602                 function isConnectedViaOtherTypes(way, node) {
58603
58604                     var wayType = typeForWay(way);
58605
58606                     if (wayType === 'highway') {
58607                         // entrances are considered connected
58608                         if (node.tags.entrance && node.tags.entrance !== 'no') { return true; }
58609                         if (node.tags.amenity === 'parking_entrance') { return true; }
58610                     } else if (wayType === 'waterway') {
58611                         if (node.id === way.first()) {
58612                             // multiple waterways may start at the same spring
58613                             if (node.tags.natural === 'spring') { return true; }
58614                         } else {
58615                             // multiple waterways may end at the same drain
58616                             if (node.tags.manhole === 'drain') { return true; }
58617                         }
58618                     }
58619
58620                     return graph.parentWays(node).some(function(parentWay) {
58621                         if (parentWay.id === way.id) { return false; }
58622
58623                         if (wayType === 'highway') {
58624
58625                             // allow connections to highway areas
58626                             if (parentWay.geometry(graph) === 'area' &&
58627                                 osmRoutableHighwayTagValues[parentWay.tags.highway]) { return true; }
58628
58629                             // count connections to ferry routes as connected
58630                             if (parentWay.tags.route === 'ferry') { return true; }
58631
58632                             return graph.parentRelations(parentWay).some(function(parentRelation) {
58633                                 if (parentRelation.tags.type === 'route' &&
58634                                     parentRelation.tags.route === 'ferry') { return true; }
58635
58636                                 // allow connections to highway multipolygons
58637                                 return parentRelation.isMultipolygon() && osmRoutableHighwayTagValues[parentRelation.tags.highway];
58638                             });
58639                         } else if (wayType === 'waterway') {
58640                             // multiple waterways may start or end at a water body at the same node
58641                             if (parentWay.tags.natural === 'water' ||
58642                                 parentWay.tags.natural === 'coastline') { return true; }
58643                         }
58644                         return false;
58645                     });
58646                 }
58647
58648                 function issuesForNode(way, nodeID) {
58649
58650                     var isFirst = nodeID === way.first();
58651
58652                     var wayType = typeForWay(way);
58653
58654                     // ignore if this way is self-connected at this node
58655                     if (nodeOccursMoreThanOnce(way, nodeID)) { return []; }
58656
58657                     var osm = services.osm;
58658                     if (!osm) { return []; }
58659
58660                     var node = graph.hasEntity(nodeID);
58661
58662                     // ignore if this node or its tile are unloaded
58663                     if (!node || !osm.isDataLoaded(node.loc)) { return []; }
58664
58665                     if (isConnectedViaOtherTypes(way, node)) { return []; }
58666
58667                     var attachedWaysOfSameType = graph.parentWays(node).filter(function(parentWay) {
58668                         if (parentWay.id === way.id) { return false; }
58669                         return typeForWay(parentWay) === wayType;
58670                     });
58671
58672                     // assume it's okay for waterways to start or end disconnected for now
58673                     if (wayType === 'waterway' && attachedWaysOfSameType.length === 0) { return []; }
58674
58675                     var attachedOneways = attachedWaysOfSameType.filter(function(attachedWay) {
58676                         return isOneway(attachedWay);
58677                     });
58678
58679                     // ignore if the way is connected to some non-oneway features
58680                     if (attachedOneways.length < attachedWaysOfSameType.length) { return []; }
58681
58682                     if (attachedOneways.length) {
58683                         var connectedEndpointsOkay = attachedOneways.some(function(attachedOneway) {
58684                             if ((isFirst ? attachedOneway.first() : attachedOneway.last()) !== nodeID) { return true; }
58685                             if (nodeOccursMoreThanOnce(attachedOneway, nodeID)) { return true; }
58686                             return false;
58687                         });
58688                         if (connectedEndpointsOkay) { return []; }
58689                     }
58690
58691                     var placement = isFirst ? 'start' : 'end',
58692                         messageID = wayType + '.',
58693                         referenceID = wayType + '.';
58694
58695                     if (wayType === 'waterway') {
58696                         messageID += 'connected.' + placement;
58697                         referenceID += 'connected';
58698                     } else {
58699                         messageID += placement;
58700                         referenceID += placement;
58701                     }
58702
58703                     return [new validationIssue({
58704                         type: type,
58705                         subtype: wayType,
58706                         severity: 'warning',
58707                         message: function(context) {
58708                             var entity = context.hasEntity(this.entityIds[0]);
58709                             return entity ? _t('issues.impossible_oneway.' + messageID + '.message', {
58710                                 feature: utilDisplayLabel(entity, context.graph())
58711                             }) : '';
58712                         },
58713                         reference: getReference(referenceID),
58714                         entityIds: [way.id, node.id],
58715                         dynamicFixes: function() {
58716
58717                             var fixes = [];
58718
58719                             if (attachedOneways.length) {
58720                                 fixes.push(new validationIssueFix({
58721                                     icon: 'iD-operation-reverse',
58722                                     title: _t('issues.fix.reverse_feature.title'),
58723                                     entityIds: [way.id],
58724                                     onClick: function(context) {
58725                                         var id = this.issue.entityIds[0];
58726                                         context.perform(actionReverse(id), _t('operations.reverse.annotation'));
58727                                     }
58728                                 }));
58729                             }
58730                             if (node.tags.noexit !== 'yes') {
58731                                 var textDirection = _mainLocalizer.textDirection();
58732                                 var useLeftContinue = (isFirst && textDirection === 'ltr') ||
58733                                     (!isFirst && textDirection === 'rtl');
58734                                 fixes.push(new validationIssueFix({
58735                                     icon: 'iD-operation-continue' + (useLeftContinue ? '-left' : ''),
58736                                     title: _t('issues.fix.continue_from_' + (isFirst ? 'start' : 'end') + '.title'),
58737                                     onClick: function(context) {
58738                                         var entityID = this.issue.entityIds[0];
58739                                         var vertexID = this.issue.entityIds[1];
58740                                         var way = context.entity(entityID);
58741                                         var vertex = context.entity(vertexID);
58742                                         continueDrawing(way, vertex, context);
58743                                     }
58744                                 }));
58745                             }
58746
58747                             return fixes;
58748                         },
58749                         loc: node.loc
58750                     })];
58751
58752                     function getReference(referenceID) {
58753                         return function showReference(selection) {
58754                             selection.selectAll('.issue-reference')
58755                                 .data([0])
58756                                 .enter()
58757                                 .append('div')
58758                                 .attr('class', 'issue-reference')
58759                                 .text(_t('issues.impossible_oneway.' + referenceID + '.reference'));
58760                         };
58761                     }
58762                 }
58763             };
58764
58765             function continueDrawing(way, vertex, context) {
58766                 // make sure the vertex is actually visible and editable
58767                 var map = context.map();
58768                 if (!context.editable() || !map.trimmedExtent().contains(vertex.loc)) {
58769                     map.zoomToEase(vertex);
58770                 }
58771
58772                 context.enter(
58773                     modeDrawLine(context, way.id, context.graph(), 'line', way.affix(vertex.id), true)
58774                 );
58775             }
58776
58777             validation.type = type;
58778
58779             return validation;
58780         }
58781
58782         function validationIncompatibleSource() {
58783             var type = 'incompatible_source';
58784             var invalidSources = [
58785                 {
58786                     id:'google', regex:'google', exceptRegex: 'books.google|Google Books|drive.google|googledrive|Google Drive'
58787                 }
58788             ];
58789
58790             var validation = function checkIncompatibleSource(entity) {
58791
58792                 var entitySources = entity.tags && entity.tags.source && entity.tags.source.split(';');
58793
58794                 if (!entitySources) { return []; }
58795
58796                 var issues = [];
58797
58798                 invalidSources.forEach(function(invalidSource) {
58799
58800                     var hasInvalidSource = entitySources.some(function(source) {
58801                         if (!source.match(new RegExp(invalidSource.regex, 'i'))) { return false; }
58802                         if (invalidSource.exceptRegex && source.match(new RegExp(invalidSource.exceptRegex, 'i'))) { return false; }
58803                         return true;
58804                     });
58805
58806                     if (!hasInvalidSource) { return; }
58807
58808                     issues.push(new validationIssue({
58809                         type: type,
58810                         severity: 'warning',
58811                         message: function(context) {
58812                             var entity = context.hasEntity(this.entityIds[0]);
58813                             return entity ? _t('issues.incompatible_source.' + invalidSource.id + '.feature.message', {
58814                                 feature: utilDisplayLabel(entity, context.graph())
58815                             }) : '';
58816                         },
58817                         reference: getReference(invalidSource.id),
58818                         entityIds: [entity.id],
58819                         dynamicFixes: function() {
58820                             return [
58821                                 new validationIssueFix({
58822                                     title: _t('issues.fix.remove_proprietary_data.title')
58823                                 })
58824                             ];
58825                         }
58826                     }));
58827                 });
58828
58829                 return issues;
58830
58831
58832                 function getReference(id) {
58833                     return function showReference(selection) {
58834                         selection.selectAll('.issue-reference')
58835                             .data([0])
58836                             .enter()
58837                             .append('div')
58838                             .attr('class', 'issue-reference')
58839                             .text(_t('issues.incompatible_source.' + id + '.reference'));
58840                     };
58841                 }
58842             };
58843
58844             validation.type = type;
58845
58846             return validation;
58847         }
58848
58849         function validationMaprules() {
58850             var type = 'maprules';
58851
58852             var validation = function checkMaprules(entity, graph) {
58853                 if (!services.maprules) { return []; }
58854
58855                 var rules = services.maprules.validationRules();
58856                 var issues = [];
58857
58858                 for (var i = 0; i < rules.length; i++) {
58859                     var rule = rules[i];
58860                     rule.findIssues(entity, graph, issues);
58861                 }
58862
58863                 return issues;
58864             };
58865
58866
58867             validation.type = type;
58868
58869             return validation;
58870         }
58871
58872         function validationMismatchedGeometry() {
58873             var type = 'mismatched_geometry';
58874
58875             function tagSuggestingLineIsArea(entity) {
58876                 if (entity.type !== 'way' || entity.isClosed()) { return null; }
58877
58878                 var tagSuggestingArea = entity.tagSuggestingArea();
58879                 if (!tagSuggestingArea) {
58880                     return null;
58881                 }
58882
58883                 var asLine = _mainPresetIndex.matchTags(tagSuggestingArea, 'line');
58884                 var asArea = _mainPresetIndex.matchTags(tagSuggestingArea, 'area');
58885                 if (asLine && asArea && (asLine === asArea)) {
58886                     // these tags also allow lines and making this an area wouldn't matter
58887                     return null;
58888                 }
58889
58890                 return tagSuggestingArea;
58891             }
58892
58893
58894             function makeConnectEndpointsFixOnClick(way, graph) {
58895                 // must have at least three nodes to close this automatically
58896                 if (way.nodes.length < 3) { return null; }
58897
58898                 var nodes = graph.childNodes(way), testNodes;
58899                 var firstToLastDistanceMeters = geoSphericalDistance(nodes[0].loc, nodes[nodes.length-1].loc);
58900
58901                 // if the distance is very small, attempt to merge the endpoints
58902                 if (firstToLastDistanceMeters < 0.75) {
58903                     testNodes = nodes.slice();   // shallow copy
58904                     testNodes.pop();
58905                     testNodes.push(testNodes[0]);
58906                     // make sure this will not create a self-intersection
58907                     if (!geoHasSelfIntersections(testNodes, testNodes[0].id)) {
58908                         return function(context) {
58909                             var way = context.entity(this.issue.entityIds[0]);
58910                             context.perform(
58911                                 actionMergeNodes([way.nodes[0], way.nodes[way.nodes.length-1]], nodes[0].loc),
58912                                 _t('issues.fix.connect_endpoints.annotation')
58913                             );
58914                         };
58915                     }
58916                 }
58917
58918                 // if the points were not merged, attempt to close the way
58919                 testNodes = nodes.slice();   // shallow copy
58920                 testNodes.push(testNodes[0]);
58921                 // make sure this will not create a self-intersection
58922                 if (!geoHasSelfIntersections(testNodes, testNodes[0].id)) {
58923                     return function(context) {
58924                         var wayId = this.issue.entityIds[0];
58925                         var way = context.entity(wayId);
58926                         var nodeId = way.nodes[0];
58927                         var index = way.nodes.length;
58928                         context.perform(
58929                             actionAddVertex(wayId, nodeId, index),
58930                             _t('issues.fix.connect_endpoints.annotation')
58931                         );
58932                     };
58933                 }
58934             }
58935
58936             function lineTaggedAsAreaIssue(entity) {
58937
58938                 var tagSuggestingArea = tagSuggestingLineIsArea(entity);
58939                 if (!tagSuggestingArea) { return null; }
58940
58941                 return new validationIssue({
58942                     type: type,
58943                     subtype: 'area_as_line',
58944                     severity: 'warning',
58945                     message: function(context) {
58946                         var entity = context.hasEntity(this.entityIds[0]);
58947                         return entity ? _t('issues.tag_suggests_area.message', {
58948                             feature: utilDisplayLabel(entity, context.graph()),
58949                             tag: utilTagText({ tags: tagSuggestingArea })
58950                         }) : '';
58951                     },
58952                     reference: showReference,
58953                     entityIds: [entity.id],
58954                     hash: JSON.stringify(tagSuggestingArea),
58955                     dynamicFixes: function(context) {
58956
58957                         var fixes = [];
58958
58959                         var entity = context.entity(this.entityIds[0]);
58960                         var connectEndsOnClick = makeConnectEndpointsFixOnClick(entity, context.graph());
58961
58962                         fixes.push(new validationIssueFix({
58963                             title: _t('issues.fix.connect_endpoints.title'),
58964                             onClick: connectEndsOnClick
58965                         }));
58966
58967                         fixes.push(new validationIssueFix({
58968                             icon: 'iD-operation-delete',
58969                             title: _t('issues.fix.remove_tag.title'),
58970                             onClick: function(context) {
58971                                 var entityId = this.issue.entityIds[0];
58972                                 var entity = context.entity(entityId);
58973                                 var tags = Object.assign({}, entity.tags);  // shallow copy
58974                                 for (var key in tagSuggestingArea) {
58975                                     delete tags[key];
58976                                 }
58977                                 context.perform(
58978                                     actionChangeTags(entityId, tags),
58979                                     _t('issues.fix.remove_tag.annotation')
58980                                 );
58981                             }
58982                         }));
58983
58984                         return fixes;
58985                     }
58986                 });
58987
58988
58989                 function showReference(selection) {
58990                     selection.selectAll('.issue-reference')
58991                         .data([0])
58992                         .enter()
58993                         .append('div')
58994                         .attr('class', 'issue-reference')
58995                         .text(_t('issues.tag_suggests_area.reference'));
58996                 }
58997             }
58998
58999             function vertexTaggedAsPointIssue(entity, graph) {
59000                 // we only care about nodes
59001                 if (entity.type !== 'node') { return null; }
59002
59003                 // ignore tagless points
59004                 if (Object.keys(entity.tags).length === 0) { return null; }
59005
59006                 // address lines are special so just ignore them
59007                 if (entity.isOnAddressLine(graph)) { return null; }
59008
59009                 var geometry = entity.geometry(graph);
59010                 var allowedGeometries = osmNodeGeometriesForTags(entity.tags);
59011
59012                 if (geometry === 'point' && !allowedGeometries.point && allowedGeometries.vertex) {
59013
59014                     return new validationIssue({
59015                         type: type,
59016                         subtype: 'vertex_as_point',
59017                         severity: 'warning',
59018                         message: function(context) {
59019                             var entity = context.hasEntity(this.entityIds[0]);
59020                             return entity ? _t('issues.vertex_as_point.message', {
59021                                 feature: utilDisplayLabel(entity, context.graph())
59022                             }) : '';
59023                         },
59024                         reference: function showReference(selection) {
59025                             selection.selectAll('.issue-reference')
59026                                 .data([0])
59027                                 .enter()
59028                                 .append('div')
59029                                 .attr('class', 'issue-reference')
59030                                 .text(_t('issues.vertex_as_point.reference'));
59031                         },
59032                         entityIds: [entity.id]
59033                     });
59034
59035                 } else if (geometry === 'vertex' && !allowedGeometries.vertex && allowedGeometries.point) {
59036
59037                     return new validationIssue({
59038                         type: type,
59039                         subtype: 'point_as_vertex',
59040                         severity: 'warning',
59041                         message: function(context) {
59042                             var entity = context.hasEntity(this.entityIds[0]);
59043                             return entity ? _t('issues.point_as_vertex.message', {
59044                                 feature: utilDisplayLabel(entity, context.graph())
59045                             }) : '';
59046                         },
59047                         reference: function showReference(selection) {
59048                             selection.selectAll('.issue-reference')
59049                                 .data([0])
59050                                 .enter()
59051                                 .append('div')
59052                                 .attr('class', 'issue-reference')
59053                                 .text(_t('issues.point_as_vertex.reference'));
59054                         },
59055                         entityIds: [entity.id],
59056                         dynamicFixes: function(context) {
59057
59058                             var entityId = this.entityIds[0];
59059
59060                             var extractOnClick = null;
59061                             if (!context.hasHiddenConnections(entityId)) {
59062
59063                                 extractOnClick = function(context) {
59064                                     var entityId = this.issue.entityIds[0];
59065                                     var action = actionExtract(entityId);
59066                                     context.perform(
59067                                         action,
59068                                         _t('operations.extract.annotation.single')
59069                                     );
59070                                     // re-enter mode to trigger updates
59071                                     context.enter(modeSelect(context, [action.getExtractedNodeID()]));
59072                                 };
59073                             }
59074
59075                             return [
59076                                 new validationIssueFix({
59077                                     icon: 'iD-operation-extract',
59078                                     title: _t('issues.fix.extract_point.title'),
59079                                     onClick: extractOnClick
59080                                 })
59081                             ];
59082                         }
59083                     });
59084                 }
59085
59086                 return null;
59087             }
59088
59089             function unclosedMultipolygonPartIssues(entity, graph) {
59090
59091                 if (entity.type !== 'relation' ||
59092                     !entity.isMultipolygon() ||
59093                     entity.isDegenerate() ||
59094                     // cannot determine issues for incompletely-downloaded relations
59095                     !entity.isComplete(graph)) { return null; }
59096
59097                 var sequences = osmJoinWays(entity.members, graph);
59098
59099                 var issues = [];
59100
59101                 for (var i in sequences) {
59102                     var sequence = sequences[i];
59103
59104                     if (!sequence.nodes) { continue; }
59105
59106                     var firstNode = sequence.nodes[0];
59107                     var lastNode = sequence.nodes[sequence.nodes.length - 1];
59108
59109                     // part is closed if the first and last nodes are the same
59110                     if (firstNode === lastNode) { continue; }
59111
59112                     var issue = new validationIssue({
59113                         type: type,
59114                         subtype: 'unclosed_multipolygon_part',
59115                         severity: 'warning',
59116                         message: function(context) {
59117                             var entity = context.hasEntity(this.entityIds[0]);
59118                             return entity ? _t('issues.unclosed_multipolygon_part.message', {
59119                                 feature: utilDisplayLabel(entity, context.graph())
59120                             }) : '';
59121                         },
59122                         reference: showReference,
59123                         loc: sequence.nodes[0].loc,
59124                         entityIds: [entity.id],
59125                         hash: sequence.map(function(way) {
59126                             return way.id;
59127                         }).join()
59128                     });
59129                     issues.push(issue);
59130                 }
59131
59132                 return issues;
59133
59134                 function showReference(selection) {
59135                     selection.selectAll('.issue-reference')
59136                         .data([0])
59137                         .enter()
59138                         .append('div')
59139                         .attr('class', 'issue-reference')
59140                         .text(_t('issues.unclosed_multipolygon_part.reference'));
59141                 }
59142             }
59143
59144             var validation = function checkMismatchedGeometry(entity, graph) {
59145                 var issues = [
59146                     vertexTaggedAsPointIssue(entity, graph),
59147                     lineTaggedAsAreaIssue(entity)
59148                 ];
59149                 issues = issues.concat(unclosedMultipolygonPartIssues(entity, graph));
59150                 return issues.filter(Boolean);
59151             };
59152
59153             validation.type = type;
59154
59155             return validation;
59156         }
59157
59158         function validationMissingRole() {
59159             var type = 'missing_role';
59160
59161             var validation = function checkMissingRole(entity, graph) {
59162                 var issues = [];
59163                 if (entity.type === 'way') {
59164                     graph.parentRelations(entity).forEach(function(relation) {
59165                         if (!relation.isMultipolygon()) { return; }
59166
59167                         var member = relation.memberById(entity.id);
59168                         if (member && isMissingRole(member)) {
59169                             issues.push(makeIssue(entity, relation, member));
59170                         }
59171                     });
59172                 } else if (entity.type === 'relation' && entity.isMultipolygon()) {
59173                     entity.indexedMembers().forEach(function(member) {
59174                         var way = graph.hasEntity(member.id);
59175                         if (way && isMissingRole(member)) {
59176                             issues.push(makeIssue(way, entity, member));
59177                         }
59178                     });
59179                 }
59180
59181                 return issues;
59182             };
59183
59184
59185             function isMissingRole(member) {
59186                 return !member.role || !member.role.trim().length;
59187             }
59188
59189
59190             function makeIssue(way, relation, member) {
59191                 return new validationIssue({
59192                     type: type,
59193                     severity: 'warning',
59194                     message: function(context) {
59195                         var member = context.hasEntity(this.entityIds[1]),
59196                             relation = context.hasEntity(this.entityIds[0]);
59197                         return (member && relation) ? _t('issues.missing_role.message', {
59198                             member: utilDisplayLabel(member, context.graph()),
59199                             relation: utilDisplayLabel(relation, context.graph())
59200                         }) : '';
59201                     },
59202                     reference: showReference,
59203                     entityIds: [relation.id, way.id],
59204                     data: {
59205                         member: member
59206                     },
59207                     hash: member.index.toString(),
59208                     dynamicFixes: function() {
59209                         return [
59210                             makeAddRoleFix('inner'),
59211                             makeAddRoleFix('outer'),
59212                             new validationIssueFix({
59213                                 icon: 'iD-operation-delete',
59214                                 title: _t('issues.fix.remove_from_relation.title'),
59215                                 onClick: function(context) {
59216                                     context.perform(
59217                                         actionDeleteMember(this.issue.entityIds[0], this.issue.data.member.index),
59218                                         _t('operations.delete_member.annotation')
59219                                     );
59220                                 }
59221                             })
59222                         ];
59223                     }
59224                 });
59225
59226
59227                 function showReference(selection) {
59228                     selection.selectAll('.issue-reference')
59229                         .data([0])
59230                         .enter()
59231                         .append('div')
59232                         .attr('class', 'issue-reference')
59233                         .text(_t('issues.missing_role.multipolygon.reference'));
59234                 }
59235             }
59236
59237
59238             function makeAddRoleFix(role) {
59239                 return new validationIssueFix({
59240                     title: _t('issues.fix.set_as_' + role + '.title'),
59241                     onClick: function(context) {
59242                         var oldMember = this.issue.data.member;
59243                         var member = { id: this.issue.entityIds[1], type: oldMember.type, role: role };
59244                         context.perform(
59245                             actionChangeMember(this.issue.entityIds[0], member, oldMember.index),
59246                             _t('operations.change_role.annotation')
59247                         );
59248                     }
59249                 });
59250             }
59251
59252             validation.type = type;
59253
59254             return validation;
59255         }
59256
59257         function validationMissingTag(context) {
59258             var type = 'missing_tag';
59259
59260             function hasDescriptiveTags(entity, graph) {
59261                 var keys = Object.keys(entity.tags)
59262                     .filter(function(k) {
59263                         if (k === 'area' || k === 'name') {
59264                             return false;
59265                         } else {
59266                             return osmIsInterestingTag(k);
59267                         }
59268                     });
59269
59270                 if (entity.type === 'relation' &&
59271                     keys.length === 1 &&
59272                     entity.tags.type === 'multipolygon') {
59273                     // this relation's only interesting tag just says its a multipolygon,
59274                     // which is not descriptive enough
59275
59276                     // It's okay for a simple multipolygon to have no descriptive tags
59277                     // if its outer way has them (old model, see `outdated_tags.js`)
59278                     return osmOldMultipolygonOuterMemberOfRelation(entity, graph);
59279                 }
59280
59281                 return keys.length > 0;
59282             }
59283
59284             function isUnknownRoad(entity) {
59285                 return entity.type === 'way' && entity.tags.highway === 'road';
59286             }
59287
59288             function isUntypedRelation(entity) {
59289                 return entity.type === 'relation' && !entity.tags.type;
59290             }
59291
59292             var validation = function checkMissingTag(entity, graph) {
59293
59294                 var subtype;
59295
59296                 var osm = context.connection();
59297                 var isUnloadedNode = entity.type === 'node' && osm && !osm.isDataLoaded(entity.loc);
59298
59299                 // we can't know if the node is a vertex if the tile is undownloaded
59300                 if (!isUnloadedNode &&
59301                     // allow untagged nodes that are part of ways
59302                     entity.geometry(graph) !== 'vertex' &&
59303                     // allow untagged entities that are part of relations
59304                     !entity.hasParentRelations(graph)) {
59305
59306                     if (Object.keys(entity.tags).length === 0) {
59307                         subtype = 'any';
59308                     } else if (!hasDescriptiveTags(entity, graph)) {
59309                         subtype = 'descriptive';
59310                     } else if (isUntypedRelation(entity)) {
59311                         subtype = 'relation_type';
59312                     }
59313                 }
59314
59315                 // flag an unknown road even if it's a member of a relation
59316                 if (!subtype && isUnknownRoad(entity)) {
59317                     subtype = 'highway_classification';
59318                 }
59319
59320                 if (!subtype) { return []; }
59321
59322                 var messageID = subtype === 'highway_classification' ? 'unknown_road' : 'missing_tag.' + subtype;
59323                 var referenceID = subtype === 'highway_classification' ? 'unknown_road' : 'missing_tag';
59324
59325                 // can always delete if the user created it in the first place..
59326                 var canDelete = (entity.version === undefined || entity.v !== undefined);
59327                 var severity = (canDelete && subtype !== 'highway_classification') ? 'error' : 'warning';
59328
59329                 return [new validationIssue({
59330                     type: type,
59331                     subtype: subtype,
59332                     severity: severity,
59333                     message: function(context) {
59334                         var entity = context.hasEntity(this.entityIds[0]);
59335                         return entity ? _t('issues.' + messageID + '.message', {
59336                             feature: utilDisplayLabel(entity, context.graph())
59337                         }) : '';
59338                     },
59339                     reference: showReference,
59340                     entityIds: [entity.id],
59341                     dynamicFixes: function(context) {
59342
59343                         var fixes = [];
59344
59345                         var selectFixType = subtype === 'highway_classification' ? 'select_road_type' : 'select_preset';
59346
59347                         fixes.push(new validationIssueFix({
59348                             icon: 'iD-icon-search',
59349                             title: _t('issues.fix.' + selectFixType + '.title'),
59350                             onClick: function(context) {
59351                                 context.ui().sidebar.showPresetList();
59352                             }
59353                         }));
59354
59355                         var deleteOnClick;
59356
59357                         var id = this.entityIds[0];
59358                         var operation = operationDelete(context, [id]);
59359                         var disabledReasonID = operation.disabled();
59360                         if (!disabledReasonID) {
59361                             deleteOnClick = function(context) {
59362                                 var id = this.issue.entityIds[0];
59363                                 var operation = operationDelete(context, [id]);
59364                                 if (!operation.disabled()) {
59365                                     operation();
59366                                 }
59367                             };
59368                         }
59369
59370                         fixes.push(
59371                             new validationIssueFix({
59372                                 icon: 'iD-operation-delete',
59373                                 title: _t('issues.fix.delete_feature.title'),
59374                                 disabledReason: disabledReasonID ? _t('operations.delete.' + disabledReasonID + '.single') : undefined,
59375                                 onClick: deleteOnClick
59376                             })
59377                         );
59378
59379                         return fixes;
59380                     }
59381                 })];
59382
59383                 function showReference(selection) {
59384                     selection.selectAll('.issue-reference')
59385                         .data([0])
59386                         .enter()
59387                         .append('div')
59388                         .attr('class', 'issue-reference')
59389                         .text(_t('issues.' + referenceID + '.reference'));
59390                 }
59391             };
59392
59393             validation.type = type;
59394
59395             return validation;
59396         }
59397
59398         // remove spaces, punctuation, diacritics
59399         var simplify = function (str) {
59400           return diacritics.remove(
59401             str
59402               .replace(/&/g, 'and')
59403               .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,'')
59404               .toLowerCase()
59405           );
59406         };
59407
59408         // toParts - split a name-suggestion-index key into parts
59409         // {
59410         //   kvnd:        "amenity/fast_food|Thaï Express~(North America)",
59411         //   kvn:         "amenity/fast_food|Thaï Express",
59412         //   kv:          "amenity/fast_food",
59413         //   k:           "amenity",
59414         //   v:           "fast_food",
59415         //   n:           "Thaï Express",
59416         //   d:           "(North America)",
59417         //   nsimple:     "thaiexpress",
59418         //   kvnnsimple:  "amenity/fast_food|thaiexpress"
59419         // }
59420         var to_parts = function (kvnd) {
59421           var parts = {};
59422           parts.kvnd = kvnd;
59423
59424           var kvndparts = kvnd.split('~', 2);
59425           if (kvndparts.length > 1) { parts.d = kvndparts[1]; }
59426
59427           parts.kvn = kvndparts[0];
59428           var kvnparts = parts.kvn.split('|', 2);
59429           if (kvnparts.length > 1) { parts.n = kvnparts[1]; }
59430
59431           parts.kv = kvnparts[0];
59432           var kvparts = parts.kv.split('/', 2);
59433           parts.k = kvparts[0];
59434           parts.v = kvparts[1];
59435
59436           parts.nsimple = simplify(parts.n);
59437           parts.kvnsimple = parts.kv + '|' + parts.nsimple;
59438           return parts;
59439         };
59440
59441         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"]};
59442         var match_groups = {
59443         matchGroups: matchGroups
59444         };
59445
59446         var match_groups$1 = /*#__PURE__*/Object.freeze({
59447                 __proto__: null,
59448                 matchGroups: matchGroups,
59449                 'default': match_groups
59450         });
59451
59452         var require$$0 = getCjsExportFromNamespace(match_groups$1);
59453
59454         var matchGroups$1 = require$$0.matchGroups;
59455
59456
59457         var matcher$1 = function () {
59458           var _warnings = [];  // array of match conflict pairs
59459           var _ambiguous = {};
59460           var _matchIndex = {};
59461           var matcher = {};
59462
59463
59464           // Create an index of all the keys/simplenames for fast matching
59465           matcher.buildMatchIndex = function (brands) {
59466             // two passes - once for primary names, once for secondary/alternate names
59467             Object.keys(brands).forEach(function (kvnd) { return insertNames(kvnd, 'primary'); });
59468             Object.keys(brands).forEach(function (kvnd) { return insertNames(kvnd, 'secondary'); });
59469
59470
59471             function insertNames(kvnd, which) {
59472               var obj = brands[kvnd];
59473               var parts = to_parts(kvnd);
59474
59475               // Exit early for ambiguous names in the second pass.
59476               // They were collected in the first pass and we don't gather alt names for them.
59477               if (which === 'secondary' && parts.d) { return; }
59478
59479
59480               if (obj.countryCodes) {
59481                 parts.countryCodes = obj.countryCodes.slice();  // copy
59482               }
59483
59484               var nomatches = (obj.nomatch || []);
59485               if (nomatches.some(function (s) { return s === kvnd; })) {
59486                 console.log(("WARNING match/nomatch conflict for " + kvnd));
59487                 return;
59488               }
59489
59490               var match_kv = [parts.kv]
59491                 .concat(obj.matchTags || [])
59492                 .concat([((parts.k) + "/yes"), "building/yes"])   // #3454 - match some generic tags
59493                 .map(function (s) { return s.toLowerCase(); });
59494
59495               var match_nsimple = [];
59496               if (which === 'primary') {
59497                 match_nsimple = [parts.n]
59498                   .concat(obj.matchNames || [])
59499                   .concat(obj.tags.official_name || [])   // #2732 - match alternate names
59500                   .map(simplify);
59501
59502               } else if (which === 'secondary') {
59503                 match_nsimple = []
59504                   .concat(obj.tags.alt_name || [])        // #2732 - match alternate names
59505                   .concat(obj.tags.short_name || [])      // #2732 - match alternate names
59506                   .map(simplify);
59507               }
59508
59509               if (!match_nsimple.length) { return; }  // nothing to do
59510
59511               match_kv.forEach(function (kv) {
59512                 match_nsimple.forEach(function (nsimple) {
59513                   if (parts.d) {
59514                     // Known ambiguous names with disambiguation string ~(USA) / ~(Canada)
59515                     // FIXME: Name collisions will overwrite the initial entry (ok for now)
59516                     if (!_ambiguous[kv]) { _ambiguous[kv] = {}; }
59517                     _ambiguous[kv][nsimple] = parts;
59518
59519                   } else {
59520                     // Names we mostly expect to be unique..
59521                     if (!_matchIndex[kv]) { _matchIndex[kv] = {}; }
59522
59523                     var m = _matchIndex[kv][nsimple];
59524                     if (m) {  // There already is a match for this name, skip it
59525                       // Warn if we detect collisions in a primary name.
59526                       // Skip warning if a secondary name or a generic `*=yes` tag - #2972 / #3454
59527                       if (which === 'primary' && !/\/yes$/.test(kv)) {
59528                         _warnings.push([m.kvnd, (kvnd + " (" + kv + "/" + nsimple + ")")]);
59529                       }
59530                     } else {
59531                       _matchIndex[kv][nsimple] = parts;   // insert
59532                     }
59533                   }
59534                 });
59535               });
59536
59537             }
59538           };
59539
59540
59541           // pass a `key`, `value`, `name` and return the best match,
59542           // `countryCode` optional (if supplied, must match that too)
59543           matcher.matchKVN = function (key, value, name, countryCode) {
59544             return matcher.matchParts(to_parts((key + "/" + value + "|" + name)), countryCode);
59545           };
59546
59547
59548           // pass a parts object and return the best match,
59549           // `countryCode` optional (if supplied, must match that too)
59550           matcher.matchParts = function (parts, countryCode) {
59551             var match = null;
59552             var inGroup = false;
59553
59554             // fixme: we currently return a single match for ambiguous
59555             match = _ambiguous[parts.kv] && _ambiguous[parts.kv][parts.nsimple];
59556             if (match && matchesCountryCode(match)) { return match; }
59557
59558             // try to return an exact match
59559             match = _matchIndex[parts.kv] && _matchIndex[parts.kv][parts.nsimple];
59560             if (match && matchesCountryCode(match)) { return match; }
59561
59562             // look in match groups
59563             for (var mg in matchGroups$1) {
59564               var matchGroup = matchGroups$1[mg];
59565               match = null;
59566               inGroup = false;
59567
59568               for (var i = 0; i < matchGroup.length; i++) {
59569                 var otherkv = matchGroup[i].toLowerCase();
59570                 if (!inGroup) {
59571                   inGroup = otherkv === parts.kv;
59572                 }
59573                 if (!match) {
59574                   // fixme: we currently return a single match for ambiguous
59575                   match = _ambiguous[otherkv] && _ambiguous[otherkv][parts.nsimple];
59576                 }
59577                 if (!match) {
59578                   match = _matchIndex[otherkv] && _matchIndex[otherkv][parts.nsimple];
59579                 }
59580
59581                 if (match && !matchesCountryCode(match)) {
59582                   match = null;
59583                 }
59584
59585                 if (inGroup && match) {
59586                   return match;
59587                 }
59588               }
59589             }
59590
59591             return null;
59592
59593             function matchesCountryCode(match) {
59594               if (!countryCode) { return true; }
59595               if (!match.countryCodes) { return true; }
59596               return match.countryCodes.indexOf(countryCode) !== -1;
59597             }
59598           };
59599
59600
59601           matcher.getWarnings = function () { return _warnings; };
59602
59603           return matcher;
59604         };
59605
59606         var quickselect$1 = createCommonjsModule(function (module, exports) {
59607         (function (global, factory) {
59608                  module.exports = factory() ;
59609         }(commonjsGlobal, (function () {
59610         function quickselect(arr, k, left, right, compare) {
59611             quickselectStep(arr, k, left || 0, right || (arr.length - 1), compare || defaultCompare);
59612         }
59613
59614         function quickselectStep(arr, k, left, right, compare) {
59615
59616             while (right > left) {
59617                 if (right - left > 600) {
59618                     var n = right - left + 1;
59619                     var m = k - left + 1;
59620                     var z = Math.log(n);
59621                     var s = 0.5 * Math.exp(2 * z / 3);
59622                     var sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1);
59623                     var newLeft = Math.max(left, Math.floor(k - m * s / n + sd));
59624                     var newRight = Math.min(right, Math.floor(k + (n - m) * s / n + sd));
59625                     quickselectStep(arr, k, newLeft, newRight, compare);
59626                 }
59627
59628                 var t = arr[k];
59629                 var i = left;
59630                 var j = right;
59631
59632                 swap(arr, left, k);
59633                 if (compare(arr[right], t) > 0) { swap(arr, left, right); }
59634
59635                 while (i < j) {
59636                     swap(arr, i, j);
59637                     i++;
59638                     j--;
59639                     while (compare(arr[i], t) < 0) { i++; }
59640                     while (compare(arr[j], t) > 0) { j--; }
59641                 }
59642
59643                 if (compare(arr[left], t) === 0) { swap(arr, left, j); }
59644                 else {
59645                     j++;
59646                     swap(arr, j, right);
59647                 }
59648
59649                 if (j <= k) { left = j + 1; }
59650                 if (k <= j) { right = j - 1; }
59651             }
59652         }
59653
59654         function swap(arr, i, j) {
59655             var tmp = arr[i];
59656             arr[i] = arr[j];
59657             arr[j] = tmp;
59658         }
59659
59660         function defaultCompare(a, b) {
59661             return a < b ? -1 : a > b ? 1 : 0;
59662         }
59663
59664         return quickselect;
59665
59666         })));
59667         });
59668
59669         var rbush_1 = rbush;
59670         var _default$2 = rbush;
59671
59672
59673
59674         function rbush(maxEntries, format) {
59675             if (!(this instanceof rbush)) { return new rbush(maxEntries, format); }
59676
59677             // max entries in a node is 9 by default; min node fill is 40% for best performance
59678             this._maxEntries = Math.max(4, maxEntries || 9);
59679             this._minEntries = Math.max(2, Math.ceil(this._maxEntries * 0.4));
59680
59681             if (format) {
59682                 this._initFormat(format);
59683             }
59684
59685             this.clear();
59686         }
59687
59688         rbush.prototype = {
59689
59690             all: function () {
59691                 return this._all(this.data, []);
59692             },
59693
59694             search: function (bbox) {
59695
59696                 var node = this.data,
59697                     result = [],
59698                     toBBox = this.toBBox;
59699
59700                 if (!intersects$1(bbox, node)) { return result; }
59701
59702                 var nodesToSearch = [],
59703                     i, len, child, childBBox;
59704
59705                 while (node) {
59706                     for (i = 0, len = node.children.length; i < len; i++) {
59707
59708                         child = node.children[i];
59709                         childBBox = node.leaf ? toBBox(child) : child;
59710
59711                         if (intersects$1(bbox, childBBox)) {
59712                             if (node.leaf) { result.push(child); }
59713                             else if (contains$2(bbox, childBBox)) { this._all(child, result); }
59714                             else { nodesToSearch.push(child); }
59715                         }
59716                     }
59717                     node = nodesToSearch.pop();
59718                 }
59719
59720                 return result;
59721             },
59722
59723             collides: function (bbox) {
59724
59725                 var node = this.data,
59726                     toBBox = this.toBBox;
59727
59728                 if (!intersects$1(bbox, node)) { return false; }
59729
59730                 var nodesToSearch = [],
59731                     i, len, child, childBBox;
59732
59733                 while (node) {
59734                     for (i = 0, len = node.children.length; i < len; i++) {
59735
59736                         child = node.children[i];
59737                         childBBox = node.leaf ? toBBox(child) : child;
59738
59739                         if (intersects$1(bbox, childBBox)) {
59740                             if (node.leaf || contains$2(bbox, childBBox)) { return true; }
59741                             nodesToSearch.push(child);
59742                         }
59743                     }
59744                     node = nodesToSearch.pop();
59745                 }
59746
59747                 return false;
59748             },
59749
59750             load: function (data) {
59751                 if (!(data && data.length)) { return this; }
59752
59753                 if (data.length < this._minEntries) {
59754                     for (var i = 0, len = data.length; i < len; i++) {
59755                         this.insert(data[i]);
59756                     }
59757                     return this;
59758                 }
59759
59760                 // recursively build the tree with the given data from scratch using OMT algorithm
59761                 var node = this._build(data.slice(), 0, data.length - 1, 0);
59762
59763                 if (!this.data.children.length) {
59764                     // save as is if tree is empty
59765                     this.data = node;
59766
59767                 } else if (this.data.height === node.height) {
59768                     // split root if trees have the same height
59769                     this._splitRoot(this.data, node);
59770
59771                 } else {
59772                     if (this.data.height < node.height) {
59773                         // swap trees if inserted one is bigger
59774                         var tmpNode = this.data;
59775                         this.data = node;
59776                         node = tmpNode;
59777                     }
59778
59779                     // insert the small tree into the large tree at appropriate level
59780                     this._insert(node, this.data.height - node.height - 1, true);
59781                 }
59782
59783                 return this;
59784             },
59785
59786             insert: function (item) {
59787                 if (item) { this._insert(item, this.data.height - 1); }
59788                 return this;
59789             },
59790
59791             clear: function () {
59792                 this.data = createNode$1([]);
59793                 return this;
59794             },
59795
59796             remove: function (item, equalsFn) {
59797                 if (!item) { return this; }
59798
59799                 var node = this.data,
59800                     bbox = this.toBBox(item),
59801                     path = [],
59802                     indexes = [],
59803                     i, parent, index, goingUp;
59804
59805                 // depth-first iterative tree traversal
59806                 while (node || path.length) {
59807
59808                     if (!node) { // go up
59809                         node = path.pop();
59810                         parent = path[path.length - 1];
59811                         i = indexes.pop();
59812                         goingUp = true;
59813                     }
59814
59815                     if (node.leaf) { // check current node
59816                         index = findItem$1(item, node.children, equalsFn);
59817
59818                         if (index !== -1) {
59819                             // item found, remove the item and condense tree upwards
59820                             node.children.splice(index, 1);
59821                             path.push(node);
59822                             this._condense(path);
59823                             return this;
59824                         }
59825                     }
59826
59827                     if (!goingUp && !node.leaf && contains$2(node, bbox)) { // go down
59828                         path.push(node);
59829                         indexes.push(i);
59830                         i = 0;
59831                         parent = node;
59832                         node = node.children[0];
59833
59834                     } else if (parent) { // go right
59835                         i++;
59836                         node = parent.children[i];
59837                         goingUp = false;
59838
59839                     } else { node = null; } // nothing found
59840                 }
59841
59842                 return this;
59843             },
59844
59845             toBBox: function (item) { return item; },
59846
59847             compareMinX: compareNodeMinX$1,
59848             compareMinY: compareNodeMinY$1,
59849
59850             toJSON: function () { return this.data; },
59851
59852             fromJSON: function (data) {
59853                 this.data = data;
59854                 return this;
59855             },
59856
59857             _all: function (node, result) {
59858                 var nodesToSearch = [];
59859                 while (node) {
59860                     if (node.leaf) { result.push.apply(result, node.children); }
59861                     else { nodesToSearch.push.apply(nodesToSearch, node.children); }
59862
59863                     node = nodesToSearch.pop();
59864                 }
59865                 return result;
59866             },
59867
59868             _build: function (items, left, right, height) {
59869
59870                 var N = right - left + 1,
59871                     M = this._maxEntries,
59872                     node;
59873
59874                 if (N <= M) {
59875                     // reached leaf level; return leaf
59876                     node = createNode$1(items.slice(left, right + 1));
59877                     calcBBox$1(node, this.toBBox);
59878                     return node;
59879                 }
59880
59881                 if (!height) {
59882                     // target height of the bulk-loaded tree
59883                     height = Math.ceil(Math.log(N) / Math.log(M));
59884
59885                     // target number of root entries to maximize storage utilization
59886                     M = Math.ceil(N / Math.pow(M, height - 1));
59887                 }
59888
59889                 node = createNode$1([]);
59890                 node.leaf = false;
59891                 node.height = height;
59892
59893                 // split the items into M mostly square tiles
59894
59895                 var N2 = Math.ceil(N / M),
59896                     N1 = N2 * Math.ceil(Math.sqrt(M)),
59897                     i, j, right2, right3;
59898
59899                 multiSelect$1(items, left, right, N1, this.compareMinX);
59900
59901                 for (i = left; i <= right; i += N1) {
59902
59903                     right2 = Math.min(i + N1 - 1, right);
59904
59905                     multiSelect$1(items, i, right2, N2, this.compareMinY);
59906
59907                     for (j = i; j <= right2; j += N2) {
59908
59909                         right3 = Math.min(j + N2 - 1, right2);
59910
59911                         // pack each entry recursively
59912                         node.children.push(this._build(items, j, right3, height - 1));
59913                     }
59914                 }
59915
59916                 calcBBox$1(node, this.toBBox);
59917
59918                 return node;
59919             },
59920
59921             _chooseSubtree: function (bbox, node, level, path) {
59922
59923                 var i, len, child, targetNode, area, enlargement, minArea, minEnlargement;
59924
59925                 while (true) {
59926                     path.push(node);
59927
59928                     if (node.leaf || path.length - 1 === level) { break; }
59929
59930                     minArea = minEnlargement = Infinity;
59931
59932                     for (i = 0, len = node.children.length; i < len; i++) {
59933                         child = node.children[i];
59934                         area = bboxArea$1(child);
59935                         enlargement = enlargedArea$1(bbox, child) - area;
59936
59937                         // choose entry with the least area enlargement
59938                         if (enlargement < minEnlargement) {
59939                             minEnlargement = enlargement;
59940                             minArea = area < minArea ? area : minArea;
59941                             targetNode = child;
59942
59943                         } else if (enlargement === minEnlargement) {
59944                             // otherwise choose one with the smallest area
59945                             if (area < minArea) {
59946                                 minArea = area;
59947                                 targetNode = child;
59948                             }
59949                         }
59950                     }
59951
59952                     node = targetNode || node.children[0];
59953                 }
59954
59955                 return node;
59956             },
59957
59958             _insert: function (item, level, isNode) {
59959
59960                 var toBBox = this.toBBox,
59961                     bbox = isNode ? item : toBBox(item),
59962                     insertPath = [];
59963
59964                 // find the best node for accommodating the item, saving all nodes along the path too
59965                 var node = this._chooseSubtree(bbox, this.data, level, insertPath);
59966
59967                 // put the item into the node
59968                 node.children.push(item);
59969                 extend$3(node, bbox);
59970
59971                 // split on node overflow; propagate upwards if necessary
59972                 while (level >= 0) {
59973                     if (insertPath[level].children.length > this._maxEntries) {
59974                         this._split(insertPath, level);
59975                         level--;
59976                     } else { break; }
59977                 }
59978
59979                 // adjust bboxes along the insertion path
59980                 this._adjustParentBBoxes(bbox, insertPath, level);
59981             },
59982
59983             // split overflowed node into two
59984             _split: function (insertPath, level) {
59985
59986                 var node = insertPath[level],
59987                     M = node.children.length,
59988                     m = this._minEntries;
59989
59990                 this._chooseSplitAxis(node, m, M);
59991
59992                 var splitIndex = this._chooseSplitIndex(node, m, M);
59993
59994                 var newNode = createNode$1(node.children.splice(splitIndex, node.children.length - splitIndex));
59995                 newNode.height = node.height;
59996                 newNode.leaf = node.leaf;
59997
59998                 calcBBox$1(node, this.toBBox);
59999                 calcBBox$1(newNode, this.toBBox);
60000
60001                 if (level) { insertPath[level - 1].children.push(newNode); }
60002                 else { this._splitRoot(node, newNode); }
60003             },
60004
60005             _splitRoot: function (node, newNode) {
60006                 // split root node
60007                 this.data = createNode$1([node, newNode]);
60008                 this.data.height = node.height + 1;
60009                 this.data.leaf = false;
60010                 calcBBox$1(this.data, this.toBBox);
60011             },
60012
60013             _chooseSplitIndex: function (node, m, M) {
60014
60015                 var i, bbox1, bbox2, overlap, area, minOverlap, minArea, index;
60016
60017                 minOverlap = minArea = Infinity;
60018
60019                 for (i = m; i <= M - m; i++) {
60020                     bbox1 = distBBox$1(node, 0, i, this.toBBox);
60021                     bbox2 = distBBox$1(node, i, M, this.toBBox);
60022
60023                     overlap = intersectionArea$1(bbox1, bbox2);
60024                     area = bboxArea$1(bbox1) + bboxArea$1(bbox2);
60025
60026                     // choose distribution with minimum overlap
60027                     if (overlap < minOverlap) {
60028                         minOverlap = overlap;
60029                         index = i;
60030
60031                         minArea = area < minArea ? area : minArea;
60032
60033                     } else if (overlap === minOverlap) {
60034                         // otherwise choose distribution with minimum area
60035                         if (area < minArea) {
60036                             minArea = area;
60037                             index = i;
60038                         }
60039                     }
60040                 }
60041
60042                 return index;
60043             },
60044
60045             // sorts node children by the best axis for split
60046             _chooseSplitAxis: function (node, m, M) {
60047
60048                 var compareMinX = node.leaf ? this.compareMinX : compareNodeMinX$1,
60049                     compareMinY = node.leaf ? this.compareMinY : compareNodeMinY$1,
60050                     xMargin = this._allDistMargin(node, m, M, compareMinX),
60051                     yMargin = this._allDistMargin(node, m, M, compareMinY);
60052
60053                 // if total distributions margin value is minimal for x, sort by minX,
60054                 // otherwise it's already sorted by minY
60055                 if (xMargin < yMargin) { node.children.sort(compareMinX); }
60056             },
60057
60058             // total margin of all possible split distributions where each node is at least m full
60059             _allDistMargin: function (node, m, M, compare) {
60060
60061                 node.children.sort(compare);
60062
60063                 var toBBox = this.toBBox,
60064                     leftBBox = distBBox$1(node, 0, m, toBBox),
60065                     rightBBox = distBBox$1(node, M - m, M, toBBox),
60066                     margin = bboxMargin$1(leftBBox) + bboxMargin$1(rightBBox),
60067                     i, child;
60068
60069                 for (i = m; i < M - m; i++) {
60070                     child = node.children[i];
60071                     extend$3(leftBBox, node.leaf ? toBBox(child) : child);
60072                     margin += bboxMargin$1(leftBBox);
60073                 }
60074
60075                 for (i = M - m - 1; i >= m; i--) {
60076                     child = node.children[i];
60077                     extend$3(rightBBox, node.leaf ? toBBox(child) : child);
60078                     margin += bboxMargin$1(rightBBox);
60079                 }
60080
60081                 return margin;
60082             },
60083
60084             _adjustParentBBoxes: function (bbox, path, level) {
60085                 // adjust bboxes along the given tree path
60086                 for (var i = level; i >= 0; i--) {
60087                     extend$3(path[i], bbox);
60088                 }
60089             },
60090
60091             _condense: function (path) {
60092                 // go through the path, removing empty nodes and updating bboxes
60093                 for (var i = path.length - 1, siblings; i >= 0; i--) {
60094                     if (path[i].children.length === 0) {
60095                         if (i > 0) {
60096                             siblings = path[i - 1].children;
60097                             siblings.splice(siblings.indexOf(path[i]), 1);
60098
60099                         } else { this.clear(); }
60100
60101                     } else { calcBBox$1(path[i], this.toBBox); }
60102                 }
60103             },
60104
60105             _initFormat: function (format) {
60106                 // data format (minX, minY, maxX, maxY accessors)
60107
60108                 // uses eval-type function compilation instead of just accepting a toBBox function
60109                 // because the algorithms are very sensitive to sorting functions performance,
60110                 // so they should be dead simple and without inner calls
60111
60112                 var compareArr = ['return a', ' - b', ';'];
60113
60114                 this.compareMinX = new Function('a', 'b', compareArr.join(format[0]));
60115                 this.compareMinY = new Function('a', 'b', compareArr.join(format[1]));
60116
60117                 this.toBBox = new Function('a',
60118                     'return {minX: a' + format[0] +
60119                     ', minY: a' + format[1] +
60120                     ', maxX: a' + format[2] +
60121                     ', maxY: a' + format[3] + '};');
60122             }
60123         };
60124
60125         function findItem$1(item, items, equalsFn) {
60126             if (!equalsFn) { return items.indexOf(item); }
60127
60128             for (var i = 0; i < items.length; i++) {
60129                 if (equalsFn(item, items[i])) { return i; }
60130             }
60131             return -1;
60132         }
60133
60134         // calculate node's bbox from bboxes of its children
60135         function calcBBox$1(node, toBBox) {
60136             distBBox$1(node, 0, node.children.length, toBBox, node);
60137         }
60138
60139         // min bounding rectangle of node children from k to p-1
60140         function distBBox$1(node, k, p, toBBox, destNode) {
60141             if (!destNode) { destNode = createNode$1(null); }
60142             destNode.minX = Infinity;
60143             destNode.minY = Infinity;
60144             destNode.maxX = -Infinity;
60145             destNode.maxY = -Infinity;
60146
60147             for (var i = k, child; i < p; i++) {
60148                 child = node.children[i];
60149                 extend$3(destNode, node.leaf ? toBBox(child) : child);
60150             }
60151
60152             return destNode;
60153         }
60154
60155         function extend$3(a, b) {
60156             a.minX = Math.min(a.minX, b.minX);
60157             a.minY = Math.min(a.minY, b.minY);
60158             a.maxX = Math.max(a.maxX, b.maxX);
60159             a.maxY = Math.max(a.maxY, b.maxY);
60160             return a;
60161         }
60162
60163         function compareNodeMinX$1(a, b) { return a.minX - b.minX; }
60164         function compareNodeMinY$1(a, b) { return a.minY - b.minY; }
60165
60166         function bboxArea$1(a)   { return (a.maxX - a.minX) * (a.maxY - a.minY); }
60167         function bboxMargin$1(a) { return (a.maxX - a.minX) + (a.maxY - a.minY); }
60168
60169         function enlargedArea$1(a, b) {
60170             return (Math.max(b.maxX, a.maxX) - Math.min(b.minX, a.minX)) *
60171                    (Math.max(b.maxY, a.maxY) - Math.min(b.minY, a.minY));
60172         }
60173
60174         function intersectionArea$1(a, b) {
60175             var minX = Math.max(a.minX, b.minX),
60176                 minY = Math.max(a.minY, b.minY),
60177                 maxX = Math.min(a.maxX, b.maxX),
60178                 maxY = Math.min(a.maxY, b.maxY);
60179
60180             return Math.max(0, maxX - minX) *
60181                    Math.max(0, maxY - minY);
60182         }
60183
60184         function contains$2(a, b) {
60185             return a.minX <= b.minX &&
60186                    a.minY <= b.minY &&
60187                    b.maxX <= a.maxX &&
60188                    b.maxY <= a.maxY;
60189         }
60190
60191         function intersects$1(a, b) {
60192             return b.minX <= a.maxX &&
60193                    b.minY <= a.maxY &&
60194                    b.maxX >= a.minX &&
60195                    b.maxY >= a.minY;
60196         }
60197
60198         function createNode$1(children) {
60199             return {
60200                 children: children,
60201                 height: 1,
60202                 leaf: true,
60203                 minX: Infinity,
60204                 minY: Infinity,
60205                 maxX: -Infinity,
60206                 maxY: -Infinity
60207             };
60208         }
60209
60210         // sort an array so that items come in groups of n unsorted items, with groups sorted between each other;
60211         // combines selection algorithm with binary divide & conquer approach
60212
60213         function multiSelect$1(arr, left, right, n, compare) {
60214             var stack = [left, right],
60215                 mid;
60216
60217             while (stack.length) {
60218                 right = stack.pop();
60219                 left = stack.pop();
60220
60221                 if (right - left <= n) { continue; }
60222
60223                 mid = left + Math.ceil((right - left) / n / 2) * n;
60224                 quickselect$1(arr, mid, left, right, compare);
60225
60226                 stack.push(left, mid, mid, right);
60227             }
60228         }
60229         rbush_1.default = _default$2;
60230
60231         var lineclip_1$1 = lineclip$1;
60232
60233         lineclip$1.polyline = lineclip$1;
60234         lineclip$1.polygon = polygonclip$1;
60235
60236
60237         // Cohen-Sutherland line clippign algorithm, adapted to efficiently
60238         // handle polylines rather than just segments
60239
60240         function lineclip$1(points, bbox, result) {
60241
60242             var len = points.length,
60243                 codeA = bitCode$1(points[0], bbox),
60244                 part = [],
60245                 i, a, b, codeB, lastCode;
60246
60247             if (!result) { result = []; }
60248
60249             for (i = 1; i < len; i++) {
60250                 a = points[i - 1];
60251                 b = points[i];
60252                 codeB = lastCode = bitCode$1(b, bbox);
60253
60254                 while (true) {
60255
60256                     if (!(codeA | codeB)) { // accept
60257                         part.push(a);
60258
60259                         if (codeB !== lastCode) { // segment went outside
60260                             part.push(b);
60261
60262                             if (i < len - 1) { // start a new line
60263                                 result.push(part);
60264                                 part = [];
60265                             }
60266                         } else if (i === len - 1) {
60267                             part.push(b);
60268                         }
60269                         break;
60270
60271                     } else if (codeA & codeB) { // trivial reject
60272                         break;
60273
60274                     } else if (codeA) { // a outside, intersect with clip edge
60275                         a = intersect$1(a, b, codeA, bbox);
60276                         codeA = bitCode$1(a, bbox);
60277
60278                     } else { // b outside
60279                         b = intersect$1(a, b, codeB, bbox);
60280                         codeB = bitCode$1(b, bbox);
60281                     }
60282                 }
60283
60284                 codeA = lastCode;
60285             }
60286
60287             if (part.length) { result.push(part); }
60288
60289             return result;
60290         }
60291
60292         // Sutherland-Hodgeman polygon clipping algorithm
60293
60294         function polygonclip$1(points, bbox) {
60295
60296             var result, edge, prev, prevInside, i, p, inside;
60297
60298             // clip against each side of the clip rectangle
60299             for (edge = 1; edge <= 8; edge *= 2) {
60300                 result = [];
60301                 prev = points[points.length - 1];
60302                 prevInside = !(bitCode$1(prev, bbox) & edge);
60303
60304                 for (i = 0; i < points.length; i++) {
60305                     p = points[i];
60306                     inside = !(bitCode$1(p, bbox) & edge);
60307
60308                     // if segment goes through the clip window, add an intersection
60309                     if (inside !== prevInside) { result.push(intersect$1(prev, p, edge, bbox)); }
60310
60311                     if (inside) { result.push(p); } // add a point if it's inside
60312
60313                     prev = p;
60314                     prevInside = inside;
60315                 }
60316
60317                 points = result;
60318
60319                 if (!points.length) { break; }
60320             }
60321
60322             return result;
60323         }
60324
60325         // intersect a segment against one of the 4 lines that make up the bbox
60326
60327         function intersect$1(a, b, edge, bbox) {
60328             return edge & 8 ? [a[0] + (b[0] - a[0]) * (bbox[3] - a[1]) / (b[1] - a[1]), bbox[3]] : // top
60329                    edge & 4 ? [a[0] + (b[0] - a[0]) * (bbox[1] - a[1]) / (b[1] - a[1]), bbox[1]] : // bottom
60330                    edge & 2 ? [bbox[2], a[1] + (b[1] - a[1]) * (bbox[2] - a[0]) / (b[0] - a[0])] : // right
60331                    edge & 1 ? [bbox[0], a[1] + (b[1] - a[1]) * (bbox[0] - a[0]) / (b[0] - a[0])] : // left
60332                    null;
60333         }
60334
60335         // bit code reflects the point position relative to the bbox:
60336
60337         //         left  mid  right
60338         //    top  1001  1000  1010
60339         //    mid  0001  0000  0010
60340         // bottom  0101  0100  0110
60341
60342         function bitCode$1(p, bbox) {
60343             var code = 0;
60344
60345             if (p[0] < bbox[0]) { code |= 1; } // left
60346             else if (p[0] > bbox[2]) { code |= 2; } // right
60347
60348             if (p[1] < bbox[1]) { code |= 4; } // bottom
60349             else if (p[1] > bbox[3]) { code |= 8; } // top
60350
60351             return code;
60352         }
60353
60354         var whichPolygon_1 = whichPolygon;
60355
60356         function whichPolygon(data) {
60357             var bboxes = [];
60358             for (var i = 0; i < data.features.length; i++) {
60359                 var feature = data.features[i];
60360                 var coords = feature.geometry.coordinates;
60361
60362                 if (feature.geometry.type === 'Polygon') {
60363                     bboxes.push(treeItem(coords, feature.properties));
60364
60365                 } else if (feature.geometry.type === 'MultiPolygon') {
60366                     for (var j = 0; j < coords.length; j++) {
60367                         bboxes.push(treeItem(coords[j], feature.properties));
60368                     }
60369                 }
60370             }
60371
60372             var tree = rbush_1().load(bboxes);
60373
60374             function query(p, multi) {
60375                 var output = [],
60376                     result = tree.search({
60377                         minX: p[0],
60378                         minY: p[1],
60379                         maxX: p[0],
60380                         maxY: p[1]
60381                     });
60382                 for (var i = 0; i < result.length; i++) {
60383                     if (insidePolygon(result[i].coords, p)) {
60384                         if (multi)
60385                             { output.push(result[i].props); }
60386                         else
60387                             { return result[i].props; }
60388                     }
60389                 }
60390                 return multi && output.length ? output : null;
60391             }
60392
60393             query.tree = tree;
60394             query.bbox = function queryBBox(bbox) {
60395                 var output = [];
60396                 var result = tree.search({
60397                     minX: bbox[0],
60398                     minY: bbox[1],
60399                     maxX: bbox[2],
60400                     maxY: bbox[3]
60401                 });
60402                 for (var i = 0; i < result.length; i++) {
60403                     if (polygonIntersectsBBox(result[i].coords, bbox)) {
60404                         output.push(result[i].props);
60405                     }
60406                 }
60407                 return output;
60408             };
60409
60410             return query;
60411         }
60412
60413         function polygonIntersectsBBox(polygon, bbox) {
60414             var bboxCenter = [
60415                 (bbox[0] + bbox[2]) / 2,
60416                 (bbox[1] + bbox[3]) / 2
60417             ];
60418             if (insidePolygon(polygon, bboxCenter)) { return true; }
60419             for (var i = 0; i < polygon.length; i++) {
60420                 if (lineclip_1$1(polygon[i], bbox).length > 0) { return true; }
60421             }
60422             return false;
60423         }
60424
60425         // ray casting algorithm for detecting if point is in polygon
60426         function insidePolygon(rings, p) {
60427             var inside = false;
60428             for (var i = 0, len = rings.length; i < len; i++) {
60429                 var ring = rings[i];
60430                 for (var j = 0, len2 = ring.length, k = len2 - 1; j < len2; k = j++) {
60431                     if (rayIntersect(p, ring[j], ring[k])) { inside = !inside; }
60432                 }
60433             }
60434             return inside;
60435         }
60436
60437         function rayIntersect(p, p1, p2) {
60438             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]);
60439         }
60440
60441         function treeItem(coords, props) {
60442             var item = {
60443                 minX: Infinity,
60444                 minY: Infinity,
60445                 maxX: -Infinity,
60446                 maxY: -Infinity,
60447                 coords: coords,
60448                 props: props
60449             };
60450
60451             for (var i = 0; i < coords[0].length; i++) {
60452                 var p = coords[0][i];
60453                 item.minX = Math.min(item.minX, p[0]);
60454                 item.minY = Math.min(item.minY, p[1]);
60455                 item.maxX = Math.max(item.maxX, p[0]);
60456                 item.maxY = Math.max(item.maxY, p[1]);
60457             }
60458             return item;
60459         }
60460
60461         var type = "FeatureCollection";
60462         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]]]]}}];
60463         var rawBorders = {
60464         type: type,
60465         features: features
60466         };
60467
60468         var borders = rawBorders;
60469         var whichPolygonGetter = {};
60470         var featuresByCode = {};
60471         var idFilterRegex = /\bThe\b|\bthe\b|\band\b|\bof\b|[-_ .,()&[\]/]/g;
60472         var levels = [
60473           'subterritory',
60474           'territory',
60475           'country',
60476           'intermediateRegion',
60477           'subregion',
60478           'region',
60479           'union',
60480           'world'
60481         ];
60482         loadDerivedDataAndCaches(borders);
60483         function loadDerivedDataAndCaches(borders) {
60484           var identifierProps = ['iso1A2', 'iso1A3', 'm49', 'wikidata', 'emojiFlag', 'nameEn'];
60485           var geometryFeatures = [];
60486           for (var i in borders.features) {
60487             var feature = borders.features[i];
60488             feature.properties.id = feature.properties.iso1A2 || feature.properties.m49;
60489             loadM49(feature);
60490             loadIsoStatus(feature);
60491             loadLevel(feature);
60492             loadGroups(feature);
60493             loadRoadSpeedUnit(feature);
60494             loadDriveSide(feature);
60495             loadFlag(feature);
60496             cacheFeatureByIDs(feature);
60497             if (feature.geometry) { geometryFeatures.push(feature); }
60498           }
60499           for (var i$1 in borders.features) {
60500             var feature$1 = borders.features[i$1];
60501             feature$1.properties.groups.sort(function(groupID1, groupID2) {
60502               return (
60503                 levels.indexOf(featuresByCode[groupID1].properties.level) -
60504                 levels.indexOf(featuresByCode[groupID2].properties.level)
60505               );
60506             });
60507             loadMembersForGroupsOf(feature$1);
60508           }
60509           var geometryOnlyCollection = {
60510             type: 'RegionFeatureCollection',
60511             features: geometryFeatures
60512           };
60513           whichPolygonGetter = whichPolygon_1(geometryOnlyCollection);
60514           function loadGroups(feature) {
60515             var props = feature.properties;
60516             if (!props.groups) {
60517               props.groups = [];
60518             }
60519             if (props.country) {
60520               props.groups.push(props.country);
60521             }
60522             if (props.m49 !== '001') {
60523               props.groups.push('001');
60524             }
60525           }
60526           function loadM49(feature) {
60527             var props = feature.properties;
60528             if (!props.m49 && props.iso1N3) {
60529               props.m49 = props.iso1N3;
60530             }
60531           }
60532           function loadIsoStatus(feature) {
60533             var props = feature.properties;
60534             if (!props.isoStatus && props.iso1A2) {
60535               props.isoStatus = 'official';
60536             }
60537           }
60538           function loadLevel(feature) {
60539             var props = feature.properties;
60540             if (props.level) { return; }
60541             if (!props.country) {
60542               props.level = 'country';
60543             } else if (props.isoStatus === 'official') {
60544               props.level = 'territory';
60545             } else {
60546               props.level = 'subterritory';
60547             }
60548           }
60549           function loadRoadSpeedUnit(feature) {
60550             var props = feature.properties;
60551             if (props.roadSpeedUnit === undefined && props.iso1A2 && props.iso1A2 !== 'EU') {
60552               props.roadSpeedUnit = 'km/h';
60553             }
60554           }
60555           function loadDriveSide(feature) {
60556             var props = feature.properties;
60557             if (props.driveSide === undefined && props.iso1A2 && props.iso1A2 !== 'EU') {
60558               props.driveSide = 'right';
60559             }
60560           }
60561           function loadFlag(feature) {
60562             if (!feature.properties.iso1A2) { return; }
60563             var flag = feature.properties.iso1A2.replace(/./g, function(char) {
60564               return String.fromCodePoint(char.charCodeAt(0) + 127397);
60565             });
60566             feature.properties.emojiFlag = flag;
60567           }
60568           function loadMembersForGroupsOf(feature) {
60569             var featureID = feature.properties.id;
60570             var standardizedGroupIDs = [];
60571             for (var j in feature.properties.groups) {
60572               var groupID = feature.properties.groups[j];
60573               var groupFeature = featuresByCode[groupID];
60574               standardizedGroupIDs.push(groupFeature.properties.id);
60575               if (groupFeature.properties.members) {
60576                 groupFeature.properties.members.push(featureID);
60577               } else {
60578                 groupFeature.properties.members = [featureID];
60579               }
60580             }
60581             feature.properties.groups = standardizedGroupIDs;
60582           }
60583           function cacheFeatureByIDs(feature) {
60584             for (var k in identifierProps) {
60585               var prop = identifierProps[k];
60586               var id = prop && feature.properties[prop];
60587               if (id) {
60588                 id = id.replace(idFilterRegex, '').toUpperCase();
60589                 featuresByCode[id] = feature;
60590               }
60591             }
60592             if (feature.properties.aliases) {
60593               for (var j in feature.properties.aliases) {
60594                 var alias = feature.properties.aliases[j].replace(idFilterRegex, '').toUpperCase();
60595                 featuresByCode[alias] = feature;
60596               }
60597             }
60598           }
60599         }
60600         function locArray(loc) {
60601           if (Array.isArray(loc)) {
60602             return loc;
60603           } else if (loc.coordinates) {
60604             return loc.coordinates;
60605           }
60606           return loc.geometry.coordinates;
60607         }
60608         function smallestFeature(loc) {
60609           var query = locArray(loc);
60610           var featureProperties = whichPolygonGetter(query);
60611           if (!featureProperties) { return null; }
60612           return featuresByCode[featureProperties.id];
60613         }
60614         function countryFeature(loc) {
60615           var feature = smallestFeature(loc);
60616           if (!feature) { return null; }
60617           var countryCode = feature.properties.country || feature.properties.iso1A2;
60618           return featuresByCode[countryCode];
60619         }
60620         function featureForLoc(loc, opts) {
60621           if (opts && opts.level && opts.level !== 'country') {
60622             var features = featuresContaining(loc);
60623             var targetLevel = opts.level;
60624             var targetLevelIndex = levels.indexOf(targetLevel);
60625             if (targetLevelIndex === -1) { return null; }
60626             for (var i in features) {
60627               var feature = features[i];
60628               if (
60629                 feature.properties.level === targetLevel ||
60630                 levels.indexOf(feature.properties.level) > targetLevelIndex
60631               ) {
60632                 return feature;
60633               }
60634             }
60635             return null;
60636           }
60637           return countryFeature(loc);
60638         }
60639         function featureForID(id) {
60640           var stringID;
60641           if (typeof id === 'number') {
60642             stringID = id.toString();
60643             if (stringID.length === 1) {
60644               stringID = '00' + stringID;
60645             } else if (stringID.length === 2) {
60646               stringID = '0' + stringID;
60647             }
60648           } else {
60649             stringID = id.replace(idFilterRegex, '').toUpperCase();
60650           }
60651           return featuresByCode[stringID] || null;
60652         }
60653         function smallestOrMatchingFeature(query) {
60654           if (typeof query === 'object') {
60655             return smallestFeature(query);
60656           }
60657           return featureForID(query);
60658         }
60659         function feature(query, opts) {
60660           if (typeof query === 'object') {
60661             return featureForLoc(query, opts);
60662           }
60663           return featureForID(query);
60664         }
60665         function iso1A2Code(query, opts) {
60666           var match = feature(query, opts);
60667           if (!match) { return null; }
60668           return match.properties.iso1A2 || null;
60669         }
60670         function featuresContaining(query, strict) {
60671           var feature = smallestOrMatchingFeature(query);
60672           if (!feature) { return []; }
60673           var features = [];
60674           if (!strict || typeof query === 'object') {
60675             features.push(feature);
60676           }
60677           var properties = feature.properties;
60678           for (var i in properties.groups) {
60679             var groupID = properties.groups[i];
60680             features.push(featuresByCode[groupID]);
60681           }
60682           return features;
60683         }
60684         function roadSpeedUnit(query) {
60685           var feature = smallestOrMatchingFeature(query);
60686           return (feature && feature.properties.roadSpeedUnit) || null;
60687         }
60688
60689         var _dataDeprecated;
60690         var _nsi;
60691
60692         function validationOutdatedTags() {
60693           var type = 'outdated_tags';
60694           var nsiKeys = ['amenity', 'shop', 'tourism', 'leisure', 'office'];
60695
60696           // A concern here in switching to async data means that `_dataDeprecated`
60697           // and `_nsi` will not be available at first, so the data on early tiles
60698           // may not have tags validated fully.
60699
60700           // initialize deprecated tags array
60701           _mainFileFetcher.get('deprecated')
60702             .then(function (d) { return _dataDeprecated = d; })
60703             .catch(function () { /* ignore */ });
60704
60705           _mainFileFetcher.get('nsi_brands')
60706             .then(function (d) {
60707               _nsi = {
60708                 brands: d.brands,
60709                 matcher: matcher$1(),
60710                 wikidata: {},
60711                 wikipedia: {}
60712               };
60713
60714               // initialize name-suggestion-index matcher
60715               _nsi.matcher.buildMatchIndex(d.brands);
60716
60717               // index all known wikipedia and wikidata tags
60718               Object.keys(d.brands).forEach(function (kvnd) {
60719                 var brand = d.brands[kvnd];
60720                 var wd = brand.tags['brand:wikidata'];
60721                 var wp = brand.tags['brand:wikipedia'];
60722                 if (wd) { _nsi.wikidata[wd] = kvnd; }
60723                 if (wp) { _nsi.wikipedia[wp] = kvnd; }
60724               });
60725
60726               return _nsi;
60727             })
60728             .catch(function () { /* ignore */ });
60729
60730
60731           function oldTagIssues(entity, graph) {
60732             var oldTags = Object.assign({}, entity.tags);  // shallow copy
60733             var preset = _mainPresetIndex.match(entity, graph);
60734             var subtype = 'deprecated_tags';
60735             if (!preset) { return []; }
60736
60737             // upgrade preset..
60738             if (preset.replacement) {
60739               var newPreset = _mainPresetIndex.item(preset.replacement);
60740               graph = actionChangePreset(entity.id, preset, newPreset, true /* skip field defaults */)(graph);
60741               entity = graph.entity(entity.id);
60742               preset = newPreset;
60743             }
60744
60745             // upgrade tags..
60746             if (_dataDeprecated) {
60747               var deprecatedTags = entity.deprecatedTags(_dataDeprecated);
60748               if (deprecatedTags.length) {
60749                 deprecatedTags.forEach(function (tag) {
60750                   graph = actionUpgradeTags(entity.id, tag.old, tag.replace)(graph);
60751                 });
60752                 entity = graph.entity(entity.id);
60753               }
60754             }
60755
60756             // add missing addTags..
60757             var newTags = Object.assign({}, entity.tags);  // shallow copy
60758             if (preset.tags !== preset.addTags) {
60759               Object.keys(preset.addTags).forEach(function (k) {
60760                 if (!newTags[k]) {
60761                   if (preset.addTags[k] === '*') {
60762                     newTags[k] = 'yes';
60763                   } else {
60764                     newTags[k] = preset.addTags[k];
60765                   }
60766                 }
60767               });
60768             }
60769
60770             if (_nsi) {
60771               // Do `wikidata` or `wikipedia` identify this entity as a brand?  #6416
60772               // If so, these tags can be swapped to `brand:wikidata`/`brand:wikipedia`
60773               var isBrand;
60774               if (newTags.wikidata) {                 // try matching `wikidata`
60775                 isBrand = _nsi.wikidata[newTags.wikidata];
60776               }
60777               if (!isBrand && newTags.wikipedia) {    // fallback to `wikipedia`
60778                 isBrand = _nsi.wikipedia[newTags.wikipedia];
60779               }
60780               if (isBrand && !newTags.office) {       // but avoid doing this for corporate offices
60781                 if (newTags.wikidata) {
60782                   newTags['brand:wikidata'] = newTags.wikidata;
60783                   delete newTags.wikidata;
60784                 }
60785                 if (newTags.wikipedia) {
60786                   newTags['brand:wikipedia'] = newTags.wikipedia;
60787                   delete newTags.wikipedia;
60788                 }
60789                 // I considered setting `name` and other tags here, but they aren't unique per wikidata
60790                 // (Q2759586 -> in USA "Papa John's", in Russia "Папа Джонс")
60791                 // So users will really need to use a preset or assign `name` themselves.
60792               }
60793
60794               // try key/value|name match against name-suggestion-index
60795               if (newTags.name) {
60796                 for (var i = 0; i < nsiKeys.length; i++) {
60797                   var k = nsiKeys[i];
60798                   if (!newTags[k]) { continue; }
60799
60800                   var center = entity.extent(graph).center();
60801                   var countryCode = iso1A2Code(center);
60802                   var match = _nsi.matcher.matchKVN(k, newTags[k], newTags.name, countryCode && countryCode.toLowerCase());
60803                   if (!match) { continue; }
60804
60805                   // for now skip ambiguous matches (like Target~(USA) vs Target~(Australia))
60806                   if (match.d) { continue; }
60807
60808                   var brand = _nsi.brands[match.kvnd];
60809                   if (brand && brand.tags['brand:wikidata'] &&
60810                     brand.tags['brand:wikidata'] !== entity.tags['not:brand:wikidata']) {
60811                     subtype = 'noncanonical_brand';
60812
60813                     var keepTags = ['takeaway'].reduce(function (acc, k) {
60814                       if (newTags[k]) {
60815                         acc[k] = newTags[k];
60816                       }
60817                       return acc;
60818                     }, {});
60819
60820                     nsiKeys.forEach(function (k) { return delete newTags[k]; });
60821                     Object.assign(newTags, brand.tags, keepTags);
60822                     break;
60823                   }
60824                 }
60825               }
60826             }
60827
60828             // determine diff
60829             var tagDiff = utilTagDiff(oldTags, newTags);
60830             if (!tagDiff.length) { return []; }
60831
60832             var isOnlyAddingTags = tagDiff.every(function (d) { return d.type === '+'; });
60833
60834             var prefix = '';
60835             if (subtype === 'noncanonical_brand') {
60836               prefix = 'noncanonical_brand.';
60837             } else if (subtype === 'deprecated_tags' && isOnlyAddingTags) {
60838               subtype = 'incomplete_tags';
60839               prefix = 'incomplete.';
60840             }
60841
60842             // don't allow autofixing brand tags
60843             var autoArgs = subtype !== 'noncanonical_brand' ? [doUpgrade, _t('issues.fix.upgrade_tags.annotation')] : null;
60844
60845             return [new validationIssue({
60846               type: type,
60847               subtype: subtype,
60848               severity: 'warning',
60849               message: showMessage,
60850               reference: showReference,
60851               entityIds: [entity.id],
60852               hash: JSON.stringify(tagDiff),
60853               dynamicFixes: function () {
60854                 return [
60855                   new validationIssueFix({
60856                     autoArgs: autoArgs,
60857                     title: _t('issues.fix.upgrade_tags.title'),
60858                     onClick: function (context) {
60859                       context.perform(doUpgrade, _t('issues.fix.upgrade_tags.annotation'));
60860                     }
60861                   })
60862                 ];
60863               }
60864             })];
60865
60866
60867             function doUpgrade(graph) {
60868               var currEntity = graph.hasEntity(entity.id);
60869               if (!currEntity) { return graph; }
60870
60871               var newTags = Object.assign({}, currEntity.tags);  // shallow copy
60872               tagDiff.forEach(function (diff) {
60873                 if (diff.type === '-') {
60874                   delete newTags[diff.key];
60875                 } else if (diff.type === '+') {
60876                   newTags[diff.key] = diff.newVal;
60877                 }
60878               });
60879
60880               return actionChangeTags(currEntity.id, newTags)(graph);
60881             }
60882
60883
60884             function showMessage(context) {
60885               var currEntity = context.hasEntity(entity.id);
60886               if (!currEntity) { return ''; }
60887
60888               var messageID = "issues.outdated_tags." + prefix + "message";
60889               if (subtype === 'noncanonical_brand' && isOnlyAddingTags) {
60890                 messageID += '_incomplete';
60891               }
60892               return _t(messageID, { feature: utilDisplayLabel(currEntity, context.graph()) });
60893             }
60894
60895
60896             function showReference(selection) {
60897               var enter = selection.selectAll('.issue-reference')
60898                 .data([0])
60899                 .enter();
60900
60901               enter
60902                 .append('div')
60903                 .attr('class', 'issue-reference')
60904                 .text(_t(("issues.outdated_tags." + prefix + "reference")));
60905
60906               enter
60907                 .append('strong')
60908                 .text(_t('issues.suggested'));
60909
60910               enter
60911                 .append('table')
60912                 .attr('class', 'tagDiff-table')
60913                 .selectAll('.tagDiff-row')
60914                 .data(tagDiff)
60915                 .enter()
60916                 .append('tr')
60917                 .attr('class', 'tagDiff-row')
60918                 .append('td')
60919                 .attr('class', function (d) {
60920                   var klass = d.type === '+' ? 'add' : 'remove';
60921                   return ("tagDiff-cell tagDiff-cell-" + klass);
60922                 })
60923                 .text(function (d) { return d.display; });
60924             }
60925           }
60926
60927
60928           function oldMultipolygonIssues(entity, graph) {
60929             var multipolygon, outerWay;
60930             if (entity.type === 'relation') {
60931               outerWay = osmOldMultipolygonOuterMemberOfRelation(entity, graph);
60932               multipolygon = entity;
60933             } else if (entity.type === 'way') {
60934               multipolygon = osmIsOldMultipolygonOuterMember(entity, graph);
60935               outerWay = entity;
60936             } else {
60937               return [];
60938             }
60939
60940             if (!multipolygon || !outerWay) { return []; }
60941
60942             return [new validationIssue({
60943               type: type,
60944               subtype: 'old_multipolygon',
60945               severity: 'warning',
60946               message: showMessage,
60947               reference: showReference,
60948               entityIds: [outerWay.id, multipolygon.id],
60949               dynamicFixes: function () {
60950                 return [
60951                   new validationIssueFix({
60952                     autoArgs: [doUpgrade, _t('issues.fix.move_tags.annotation')],
60953                     title: _t('issues.fix.move_tags.title'),
60954                     onClick: function (context) {
60955                       context.perform(doUpgrade, _t('issues.fix.move_tags.annotation'));
60956                     }
60957                   })
60958                 ];
60959               }
60960             })];
60961
60962
60963             function doUpgrade(graph) {
60964               var currMultipolygon = graph.hasEntity(multipolygon.id);
60965               var currOuterWay = graph.hasEntity(outerWay.id);
60966               if (!currMultipolygon || !currOuterWay) { return graph; }
60967
60968               currMultipolygon = currMultipolygon.mergeTags(currOuterWay.tags);
60969               graph = graph.replace(currMultipolygon);
60970               return actionChangeTags(currOuterWay.id, {})(graph);
60971             }
60972
60973
60974             function showMessage(context) {
60975               var currMultipolygon = context.hasEntity(multipolygon.id);
60976               if (!currMultipolygon) { return ''; }
60977
60978               return _t('issues.old_multipolygon.message',
60979                   { multipolygon: utilDisplayLabel(currMultipolygon, context.graph()) }
60980               );
60981             }
60982
60983
60984             function showReference(selection) {
60985               selection.selectAll('.issue-reference')
60986                 .data([0])
60987                 .enter()
60988                 .append('div')
60989                 .attr('class', 'issue-reference')
60990                 .text(_t('issues.old_multipolygon.reference'));
60991             }
60992           }
60993
60994
60995           var validation = function checkOutdatedTags(entity, graph) {
60996             var issues = oldMultipolygonIssues(entity, graph);
60997             if (!issues.length) { issues = oldTagIssues(entity, graph); }
60998             return issues;
60999           };
61000
61001
61002           validation.type = type;
61003
61004           return validation;
61005         }
61006
61007         function validationPrivateData() {
61008             var type = 'private_data';
61009
61010             // assume that some buildings are private
61011             var privateBuildingValues = {
61012                 detached: true,
61013                 farm: true,
61014                 house: true,
61015                 houseboat: true,
61016                 residential: true,
61017                 semidetached_house: true,
61018                 static_caravan: true
61019             };
61020
61021             // but they might be public if they have one of these other tags
61022             var publicKeys = {
61023                 amenity: true,
61024                 craft: true,
61025                 historic: true,
61026                 leisure: true,
61027                 office: true,
61028                 shop: true,
61029                 tourism: true
61030             };
61031
61032             // these tags may contain personally identifying info
61033             var personalTags = {
61034                 'contact:email': true,
61035                 'contact:fax': true,
61036                 'contact:phone': true,
61037                 email: true,
61038                 fax: true,
61039                 phone: true
61040             };
61041
61042
61043             var validation = function checkPrivateData(entity) {
61044                 var tags = entity.tags;
61045                 if (!tags.building || !privateBuildingValues[tags.building]) { return []; }
61046
61047                 var keepTags = {};
61048                 for (var k in tags) {
61049                     if (publicKeys[k]) { return []; }  // probably a public feature
61050                     if (!personalTags[k]) {
61051                         keepTags[k] = tags[k];
61052                     }
61053                 }
61054
61055                 var tagDiff = utilTagDiff(tags, keepTags);
61056                 if (!tagDiff.length) { return []; }
61057
61058                 var fixID = tagDiff.length === 1 ? 'remove_tag' : 'remove_tags';
61059
61060                 return [new validationIssue({
61061                     type: type,
61062                     severity: 'warning',
61063                     message: showMessage,
61064                     reference: showReference,
61065                     entityIds: [entity.id],
61066                     dynamicFixes: function() {
61067                         return [
61068                             new validationIssueFix({
61069                                 icon: 'iD-operation-delete',
61070                                 title: _t('issues.fix.' + fixID + '.title'),
61071                                 onClick: function(context) {
61072                                     context.perform(doUpgrade, _t('issues.fix.upgrade_tags.annotation'));
61073                                 }
61074                             })
61075                         ];
61076                     }
61077                 })];
61078
61079
61080                 function doUpgrade(graph) {
61081                     var currEntity = graph.hasEntity(entity.id);
61082                     if (!currEntity) { return graph; }
61083
61084                     var newTags = Object.assign({}, currEntity.tags);  // shallow copy
61085                     tagDiff.forEach(function(diff) {
61086                         if (diff.type === '-') {
61087                             delete newTags[diff.key];
61088                         } else if (diff.type === '+') {
61089                             newTags[diff.key] = diff.newVal;
61090                         }
61091                     });
61092
61093                     return actionChangeTags(currEntity.id, newTags)(graph);
61094                 }
61095
61096
61097                 function showMessage(context) {
61098                     var currEntity = context.hasEntity(this.entityIds[0]);
61099                     if (!currEntity) { return ''; }
61100
61101                     return _t('issues.private_data.contact.message',
61102                         { feature: utilDisplayLabel(currEntity, context.graph()) }
61103                     );
61104                 }
61105
61106
61107                 function showReference(selection) {
61108                     var enter = selection.selectAll('.issue-reference')
61109                         .data([0])
61110                         .enter();
61111
61112                     enter
61113                         .append('div')
61114                         .attr('class', 'issue-reference')
61115                         .text(_t('issues.private_data.reference'));
61116
61117                     enter
61118                         .append('strong')
61119                         .text(_t('issues.suggested'));
61120
61121                     enter
61122                         .append('table')
61123                         .attr('class', 'tagDiff-table')
61124                         .selectAll('.tagDiff-row')
61125                         .data(tagDiff)
61126                         .enter()
61127                         .append('tr')
61128                         .attr('class', 'tagDiff-row')
61129                         .append('td')
61130                         .attr('class', function(d) {
61131                             var klass = d.type === '+' ? 'add' : 'remove';
61132                             return 'tagDiff-cell tagDiff-cell-' + klass;
61133                         })
61134                         .text(function(d) { return d.display; });
61135                 }
61136             };
61137
61138
61139             validation.type = type;
61140
61141             return validation;
61142         }
61143
61144         var _discardNameRegexes = [];
61145
61146         function validationSuspiciousName() {
61147           var type = 'suspicious_name';
61148           var keysToTestForGenericValues = [
61149             'aerialway', 'aeroway', 'amenity', 'building', 'craft', 'highway',
61150             'leisure', 'railway', 'man_made', 'office', 'shop', 'tourism', 'waterway'
61151           ];
61152
61153           // A concern here in switching to async data means that `_nsiFilters` will not
61154           // be available at first, so the data on early tiles may not have tags validated fully.
61155
61156           _mainFileFetcher.get('nsi_filters')
61157             .then(function (filters) {
61158               // known list of generic names (e.g. "bar")
61159               _discardNameRegexes = filters.discardNames
61160                 .map(function (discardName) { return new RegExp(discardName, 'i'); });
61161             })
61162             .catch(function () { /* ignore */ });
61163
61164
61165           function isDiscardedSuggestionName(lowercaseName) {
61166             return _discardNameRegexes.some(function (regex) { return regex.test(lowercaseName); });
61167           }
61168
61169           // test if the name is just the key or tag value (e.g. "park")
61170           function nameMatchesRawTag(lowercaseName, tags) {
61171             for (var i = 0; i < keysToTestForGenericValues.length; i++) {
61172               var key = keysToTestForGenericValues[i];
61173               var val = tags[key];
61174               if (val) {
61175                 val = val.toLowerCase();
61176                 if (key === lowercaseName ||
61177                   val === lowercaseName ||
61178                   key.replace(/\_/g, ' ') === lowercaseName ||
61179                   val.replace(/\_/g, ' ') === lowercaseName) {
61180                   return true;
61181                 }
61182               }
61183             }
61184             return false;
61185           }
61186
61187           function isGenericName(name, tags) {
61188             name = name.toLowerCase();
61189             return nameMatchesRawTag(name, tags) || isDiscardedSuggestionName(name);
61190           }
61191
61192           function makeGenericNameIssue(entityId, nameKey, genericName, langCode) {
61193             return new validationIssue({
61194               type: type,
61195               subtype: 'generic_name',
61196               severity: 'warning',
61197               message: function(context) {
61198                 var entity = context.hasEntity(this.entityIds[0]);
61199                 if (!entity) { return ''; }
61200                 var preset = _mainPresetIndex.match(entity, context.graph());
61201                 var langName = langCode && _mainLocalizer.languageName(langCode);
61202                 return _t('issues.generic_name.message' + (langName ? '_language' : ''),
61203                   { feature: preset.name(), name: genericName, language: langName }
61204                 );
61205               },
61206               reference: showReference,
61207               entityIds: [entityId],
61208               hash: nameKey + '=' + genericName,
61209               dynamicFixes: function() {
61210                 return [
61211                   new validationIssueFix({
61212                     icon: 'iD-operation-delete',
61213                     title: _t('issues.fix.remove_the_name.title'),
61214                     onClick: function(context) {
61215                       var entityId = this.issue.entityIds[0];
61216                       var entity = context.entity(entityId);
61217                       var tags = Object.assign({}, entity.tags);   // shallow copy
61218                       delete tags[nameKey];
61219                       context.perform(
61220                         actionChangeTags(entityId, tags), _t('issues.fix.remove_generic_name.annotation')
61221                       );
61222                     }
61223                   })
61224                 ];
61225               }
61226             });
61227
61228             function showReference(selection) {
61229               selection.selectAll('.issue-reference')
61230                 .data([0])
61231                 .enter()
61232                 .append('div')
61233                 .attr('class', 'issue-reference')
61234                 .text(_t('issues.generic_name.reference'));
61235             }
61236           }
61237
61238           function makeIncorrectNameIssue(entityId, nameKey, incorrectName, langCode) {
61239             return new validationIssue({
61240               type: type,
61241               subtype: 'not_name',
61242               severity: 'warning',
61243               message: function(context) {
61244                 var entity = context.hasEntity(this.entityIds[0]);
61245                 if (!entity) { return ''; }
61246                 var preset = _mainPresetIndex.match(entity, context.graph());
61247                 var langName = langCode && _mainLocalizer.languageName(langCode);
61248                 return _t('issues.incorrect_name.message' + (langName ? '_language' : ''),
61249                   { feature: preset.name(), name: incorrectName, language: langName }
61250                 );
61251               },
61252               reference: showReference,
61253               entityIds: [entityId],
61254               hash: nameKey + '=' + incorrectName,
61255               dynamicFixes: function() {
61256                 return [
61257                   new validationIssueFix({
61258                     icon: 'iD-operation-delete',
61259                     title: _t('issues.fix.remove_the_name.title'),
61260                     onClick: function(context) {
61261                       var entityId = this.issue.entityIds[0];
61262                       var entity = context.entity(entityId);
61263                       var tags = Object.assign({}, entity.tags);   // shallow copy
61264                       delete tags[nameKey];
61265                       context.perform(
61266                         actionChangeTags(entityId, tags), _t('issues.fix.remove_mistaken_name.annotation')
61267                       );
61268                     }
61269                   })
61270                 ];
61271               }
61272             });
61273
61274             function showReference(selection) {
61275               selection.selectAll('.issue-reference')
61276                 .data([0])
61277                 .enter()
61278                 .append('div')
61279                 .attr('class', 'issue-reference')
61280                 .text(_t('issues.generic_name.reference'));
61281             }
61282           }
61283
61284
61285           var validation = function checkGenericName(entity) {
61286             // a generic name is okay if it's a known brand or entity
61287             if (entity.hasWikidata()) { return []; }
61288
61289             var issues = [];
61290             var notNames = (entity.tags['not:name'] || '').split(';');
61291
61292             for (var key in entity.tags) {
61293               var m = key.match(/^name(?:(?::)([a-zA-Z_-]+))?$/);
61294               if (!m) { continue; }
61295
61296               var langCode = m.length >= 2 ? m[1] : null;
61297               var value = entity.tags[key];
61298               if (notNames.length) {
61299                 for (var i in notNames) {
61300                   var notName = notNames[i];
61301                   if (notName && value === notName) {
61302                     issues.push(makeIncorrectNameIssue(entity.id, key, value, langCode));
61303                     continue;
61304                   }
61305                 }
61306               }
61307               if (isGenericName(value, entity.tags)) {
61308                 issues.push(makeGenericNameIssue(entity.id, key, value, langCode));
61309               }
61310             }
61311
61312             return issues;
61313           };
61314
61315
61316           validation.type = type;
61317
61318           return validation;
61319         }
61320
61321         function validationUnsquareWay(context) {
61322             var type = 'unsquare_way';
61323             var DEFAULT_DEG_THRESHOLD = 5;   // see also issues.js
61324
61325             // use looser epsilon for detection to reduce warnings of buildings that are essentially square already
61326             var epsilon = 0.05;
61327             var nodeThreshold = 10;
61328
61329             function isBuilding(entity, graph) {
61330                 if (entity.type !== 'way' || entity.geometry(graph) !== 'area') { return false; }
61331                 return entity.tags.building && entity.tags.building !== 'no';
61332             }
61333
61334
61335             var validation = function checkUnsquareWay(entity, graph) {
61336
61337                 if (!isBuilding(entity, graph)) { return []; }
61338
61339                 // don't flag ways marked as physically unsquare
61340                 if (entity.tags.nonsquare === 'yes') { return []; }
61341
61342                 var isClosed = entity.isClosed();
61343                 if (!isClosed) { return []; }        // this building has bigger problems
61344
61345                 // don't flag ways with lots of nodes since they are likely detail-mapped
61346                 var nodes = graph.childNodes(entity).slice();    // shallow copy
61347                 if (nodes.length > nodeThreshold + 1) { return []; }   // +1 because closing node appears twice
61348
61349                 // ignore if not all nodes are fully downloaded
61350                 var osm = services.osm;
61351                 if (!osm || nodes.some(function(node) { return !osm.isDataLoaded(node.loc); })) { return []; }
61352
61353                 // don't flag connected ways to avoid unresolvable unsquare loops
61354                 var hasConnectedSquarableWays = nodes.some(function(node) {
61355                     return graph.parentWays(node).some(function(way) {
61356                         if (way.id === entity.id) { return false; }
61357                         if (isBuilding(way, graph)) { return true; }
61358                         return graph.parentRelations(way).some(function(parentRelation) {
61359                             return parentRelation.isMultipolygon() &&
61360                                 parentRelation.tags.building &&
61361                                 parentRelation.tags.building !== 'no';
61362                         });
61363                     });
61364                 });
61365                 if (hasConnectedSquarableWays) { return []; }
61366
61367
61368                 // user-configurable square threshold
61369                 var storedDegreeThreshold = corePreferences('validate-square-degrees');
61370                 var degreeThreshold = isNaN(storedDegreeThreshold) ? DEFAULT_DEG_THRESHOLD : parseFloat(storedDegreeThreshold);
61371
61372                 var points = nodes.map(function(node) { return context.projection(node.loc); });
61373                 if (!geoOrthoCanOrthogonalize(points, isClosed, epsilon, degreeThreshold, true)) { return []; }
61374
61375                 var autoArgs;
61376                 // don't allow autosquaring features linked to wikidata
61377                 if (!entity.tags.wikidata) {
61378                     // use same degree threshold as for detection
61379                     var autoAction = actionOrthogonalize(entity.id, context.projection, undefined, degreeThreshold);
61380                     autoAction.transitionable = false;  // when autofixing, do it instantly
61381                     autoArgs = [autoAction, _t('operations.orthogonalize.annotation.feature.single')];
61382                 }
61383
61384                 return [new validationIssue({
61385                     type: type,
61386                     subtype: 'building',
61387                     severity: 'warning',
61388                     message: function(context) {
61389                         var entity = context.hasEntity(this.entityIds[0]);
61390                         return entity ? _t('issues.unsquare_way.message', { feature: utilDisplayLabel(entity, context.graph()) }) : '';
61391                     },
61392                     reference: showReference,
61393                     entityIds: [entity.id],
61394                     hash: JSON.stringify(autoArgs !== undefined) + degreeThreshold,
61395                     dynamicFixes: function() {
61396                         return [
61397                             new validationIssueFix({
61398                                 icon: 'iD-operation-orthogonalize',
61399                                 title: _t('issues.fix.square_feature.title'),
61400                                 autoArgs: autoArgs,
61401                                 onClick: function(context, completionHandler) {
61402                                     var entityId = this.issue.entityIds[0];
61403                                     // use same degree threshold as for detection
61404                                     context.perform(
61405                                         actionOrthogonalize(entityId, context.projection, undefined, degreeThreshold),
61406                                         _t('operations.orthogonalize.annotation.feature.single')
61407                                     );
61408                                     // run after the squaring transition (currently 150ms)
61409                                     window.setTimeout(function() { completionHandler(); }, 175);
61410                                 }
61411                             }) ];
61412                     }
61413                 })];
61414
61415                 function showReference(selection) {
61416                     selection.selectAll('.issue-reference')
61417                         .data([0])
61418                         .enter()
61419                         .append('div')
61420                         .attr('class', 'issue-reference')
61421                         .text(_t('issues.unsquare_way.buildings.reference'));
61422                 }
61423             };
61424
61425             validation.type = type;
61426
61427             return validation;
61428         }
61429
61430         var Validations = /*#__PURE__*/Object.freeze({
61431                 __proto__: null,
61432                 validationAlmostJunction: validationAlmostJunction,
61433                 validationCloseNodes: validationCloseNodes,
61434                 validationCrossingWays: validationCrossingWays,
61435                 validationDisconnectedWay: validationDisconnectedWay,
61436                 validationFormatting: validationFormatting,
61437                 validationHelpRequest: validationHelpRequest,
61438                 validationImpossibleOneway: validationImpossibleOneway,
61439                 validationIncompatibleSource: validationIncompatibleSource,
61440                 validationMaprules: validationMaprules,
61441                 validationMismatchedGeometry: validationMismatchedGeometry,
61442                 validationMissingRole: validationMissingRole,
61443                 validationMissingTag: validationMissingTag,
61444                 validationOutdatedTags: validationOutdatedTags,
61445                 validationPrivateData: validationPrivateData,
61446                 validationSuspiciousName: validationSuspiciousName,
61447                 validationUnsquareWay: validationUnsquareWay
61448         });
61449
61450         function coreValidator(context) {
61451             var dispatch$1 = dispatch('validated', 'focusedIssue');
61452             var validator = utilRebind({}, dispatch$1, 'on');
61453
61454             var _rules = {};
61455             var _disabledRules = {};
61456
61457             var _ignoredIssueIDs = {};          // issue.id -> true
61458             var _baseCache = validationCache(); // issues before any user edits
61459             var _headCache = validationCache(); // issues after all user edits
61460             var _validatedGraph = null;
61461             var _deferred = new Set();
61462
61463             //
61464             // initialize the validator rulesets
61465             //
61466             validator.init = function() {
61467                 Object.values(Validations).forEach(function(validation) {
61468                     if (typeof validation !== 'function') { return; }
61469
61470                     var fn = validation(context);
61471                     var key = fn.type;
61472                     _rules[key] = fn;
61473                 });
61474
61475                 var disabledRules = corePreferences('validate-disabledRules');
61476                 if (disabledRules) {
61477                     disabledRules.split(',')
61478                         .forEach(function(key) { _disabledRules[key] = true; });
61479                 }
61480             };
61481
61482
61483             //
61484             // clear caches, called whenever iD resets after a save
61485             //
61486             validator.reset = function() {
61487                 Array.from(_deferred).forEach(function(handle) {
61488                     window.cancelIdleCallback(handle);
61489                     _deferred.delete(handle);
61490                 });
61491
61492                 // clear caches
61493                 _ignoredIssueIDs = {};
61494                 _baseCache = validationCache();
61495                 _headCache = validationCache();
61496                 _validatedGraph = null;
61497             };
61498
61499             validator.resetIgnoredIssues = function() {
61500                 _ignoredIssueIDs = {};
61501                 // reload UI
61502                 dispatch$1.call('validated');
61503             };
61504
61505
61506             // must update issues when the user changes the unsquare thereshold
61507             validator.reloadUnsquareIssues = function() {
61508
61509                 reloadUnsquareIssues(_headCache, context.graph());
61510                 reloadUnsquareIssues(_baseCache, context.history().base());
61511
61512                 dispatch$1.call('validated');
61513             };
61514
61515             function reloadUnsquareIssues(cache, graph) {
61516
61517                 var checkUnsquareWay = _rules.unsquare_way;
61518                 if (typeof checkUnsquareWay !== 'function') { return; }
61519
61520                 // uncache existing
61521                 cache.uncacheIssuesOfType('unsquare_way');
61522
61523                 var buildings = context.history().tree().intersects(geoExtent([-180,-90],[180, 90]), graph)  // everywhere
61524                     .filter(function(entity) {
61525                         return entity.type === 'way' && entity.tags.building && entity.tags.building !== 'no';
61526                     });
61527
61528                 // rerun for all buildings
61529                 buildings.forEach(function(entity) {
61530                     var detected = checkUnsquareWay(entity, graph);
61531                     if (detected.length !== 1) { return; }
61532                     var issue = detected[0];
61533                     if (!cache.issuesByEntityID[entity.id]) {
61534                         cache.issuesByEntityID[entity.id] = new Set();
61535                     }
61536                     cache.issuesByEntityID[entity.id].add(issue.id);
61537                     cache.issuesByIssueID[issue.id] = issue;
61538                 });
61539             }
61540
61541             // options = {
61542             //     what: 'all',     // 'all' or 'edited'
61543             //     where: 'all',   // 'all' or 'visible'
61544             //     includeIgnored: false   // true, false, or 'only'
61545             //     includeDisabledRules: false   // true, false, or 'only'
61546             // };
61547             validator.getIssues = function(options) {
61548                 var opts = Object.assign({ what: 'all', where: 'all', includeIgnored: false, includeDisabledRules: false }, options);
61549                 var issues = Object.values(_headCache.issuesByIssueID);
61550                 var view = context.map().extent();
61551
61552                 return issues.filter(function(issue) {
61553                     if (!issue) { return false; }
61554                     if (opts.includeDisabledRules === 'only' && !_disabledRules[issue.type]) { return false; }
61555                     if (!opts.includeDisabledRules && _disabledRules[issue.type]) { return false; }
61556
61557                     if (opts.includeIgnored === 'only' && !_ignoredIssueIDs[issue.id]) { return false; }
61558                     if (!opts.includeIgnored && _ignoredIssueIDs[issue.id]) { return false; }
61559
61560                     // Sanity check:  This issue may be for an entity that not longer exists.
61561                     // If we detect this, uncache and return false so it is not included..
61562                     var entityIds = issue.entityIds || [];
61563                     for (var i = 0; i < entityIds.length; i++) {
61564                         var entityId = entityIds[i];
61565                         if (!context.hasEntity(entityId)) {
61566                             delete _headCache.issuesByEntityID[entityId];
61567                             delete _headCache.issuesByIssueID[issue.id];
61568                             return false;
61569                         }
61570                     }
61571
61572                     if (opts.what === 'edited' && _baseCache.issuesByIssueID[issue.id]) { return false; }
61573
61574                     if (opts.where === 'visible') {
61575                         var extent = issue.extent(context.graph());
61576                         if (!view.intersects(extent)) { return false; }
61577                     }
61578
61579                     return true;
61580                 });
61581             };
61582
61583             validator.getResolvedIssues = function() {
61584                 var baseIssues = Object.values(_baseCache.issuesByIssueID);
61585                 return baseIssues.filter(function(issue) {
61586                     return !_headCache.issuesByIssueID[issue.id];
61587                 });
61588             };
61589
61590             validator.focusIssue = function(issue) {
61591                 var extent = issue.extent(context.graph());
61592
61593                 if (extent) {
61594                     var setZoom = Math.max(context.map().zoom(), 19);
61595                     context.map().unobscuredCenterZoomEase(extent.center(), setZoom);
61596
61597                     // select the first entity
61598                     if (issue.entityIds && issue.entityIds.length) {
61599                         window.setTimeout(function() {
61600                             var ids = issue.entityIds;
61601                             context.enter(modeSelect(context, [ids[0]]));
61602                             dispatch$1.call('focusedIssue', this, issue);
61603                         }, 250);  // after ease
61604                     }
61605                 }
61606             };
61607
61608
61609             validator.getIssuesBySeverity = function(options) {
61610                 var groups = utilArrayGroupBy(validator.getIssues(options), 'severity');
61611                 groups.error = groups.error || [];
61612                 groups.warning = groups.warning || [];
61613                 return groups;
61614             };
61615
61616             // show some issue types in a particular order
61617             var orderedIssueTypes = [
61618                 // flag missing data first
61619                 'missing_tag', 'missing_role',
61620                 // then flag identity issues
61621                 'outdated_tags', 'mismatched_geometry',
61622                 // flag geometry issues where fixing them might solve connectivity issues
61623                 'crossing_ways', 'almost_junction',
61624                 // then flag connectivity issues
61625                 'disconnected_way', 'impossible_oneway'
61626             ];
61627
61628             // returns the issues that the given entity IDs have in common, matching the given options
61629             validator.getSharedEntityIssues = function(entityIDs, options) {
61630                 var cache = _headCache;
61631
61632                 // gather the issues that are common to all the entities
61633                 var issueIDs = entityIDs.reduce(function(acc, entityID) {
61634                     var entityIssueIDs = cache.issuesByEntityID[entityID] || new Set();
61635                     if (!acc) {
61636                         return new Set(entityIssueIDs);
61637                     }
61638                     return new Set([].concat( acc ).filter(function(elem) {
61639                         return entityIssueIDs.has(elem);
61640                     }));
61641                 }, null) || [];
61642
61643                 var opts = options || {};
61644
61645                 return Array.from(issueIDs)
61646                     .map(function(id) { return cache.issuesByIssueID[id]; })
61647                     .filter(function(issue) {
61648                         if (!issue) { return false; }
61649                         if (opts.includeDisabledRules === 'only' && !_disabledRules[issue.type]) { return false; }
61650                         if (!opts.includeDisabledRules && _disabledRules[issue.type]) { return false; }
61651
61652                         if (opts.includeIgnored === 'only' && !_ignoredIssueIDs[issue.id]) { return false; }
61653                         if (!opts.includeIgnored && _ignoredIssueIDs[issue.id]) { return false; }
61654
61655                         return true;
61656                     }).sort(function(issue1, issue2) {
61657                         if (issue1.type === issue2.type) {
61658                             // issues of the same type, sort deterministically
61659                             return issue1.id < issue2.id ? -1 : 1;
61660                         }
61661                         var index1 = orderedIssueTypes.indexOf(issue1.type);
61662                         var index2 = orderedIssueTypes.indexOf(issue2.type);
61663                         if (index1 !== -1 && index2 !== -1) {
61664                             // both issue types have explicit sort orders
61665                             return index1 - index2;
61666                         } else if (index1 === -1 && index2 === -1) {
61667                             // neither issue type has an explicit sort order, sort by type
61668                             return issue1.type < issue2.type ? -1 : 1;
61669                         } else {
61670                             // order explicit types before everything else
61671                             return index1 !== -1 ? -1 : 1;
61672                         }
61673                     });
61674             };
61675
61676
61677             validator.getEntityIssues = function(entityID, options) {
61678                 return validator.getSharedEntityIssues([entityID], options);
61679             };
61680
61681
61682             validator.getRuleKeys = function() {
61683                 return Object.keys(_rules);
61684             };
61685
61686
61687             validator.isRuleEnabled = function(key) {
61688                 return !_disabledRules[key];
61689             };
61690
61691
61692             validator.toggleRule = function(key) {
61693                 if (_disabledRules[key]) {
61694                     delete _disabledRules[key];
61695                 } else {
61696                     _disabledRules[key] = true;
61697                 }
61698
61699                 corePreferences('validate-disabledRules', Object.keys(_disabledRules).join(','));
61700                 validator.validate();
61701             };
61702
61703
61704             validator.disableRules = function(keys) {
61705                 _disabledRules = {};
61706                 keys.forEach(function(k) {
61707                     _disabledRules[k] = true;
61708                 });
61709
61710                 corePreferences('validate-disabledRules', Object.keys(_disabledRules).join(','));
61711                 validator.validate();
61712             };
61713
61714
61715             validator.ignoreIssue = function(id) {
61716                 _ignoredIssueIDs[id] = true;
61717             };
61718
61719
61720             //
61721             // Run validation on a single entity for the given graph
61722             //
61723             function validateEntity(entity, graph) {
61724                 var entityIssues = [];
61725
61726                 // runs validation and appends resulting issues
61727                 function runValidation(key) {
61728
61729                     var fn = _rules[key];
61730                     if (typeof fn !== 'function') {
61731                         console.error('no such validation rule = ' + key);  // eslint-disable-line no-console
61732                         return;
61733                     }
61734
61735                     var detected = fn(entity, graph);
61736                     entityIssues = entityIssues.concat(detected);
61737                 }
61738
61739                 // run all rules
61740                 Object.keys(_rules).forEach(runValidation);
61741
61742                 return entityIssues;
61743             }
61744
61745             function entityIDsToValidate(entityIDs, graph) {
61746                 var processedIDs = new Set();
61747                 return entityIDs.reduce(function(acc, entityID) {
61748                     // keep redundancy check separate from `acc` because an `entityID`
61749                     // could have been added to `acc` as a related entity through an earlier pass
61750                     if (processedIDs.has(entityID)) { return acc; }
61751                     processedIDs.add(entityID);
61752
61753                     var entity = graph.hasEntity(entityID);
61754                     if (!entity) { return acc; }
61755
61756                     acc.add(entityID);
61757
61758                     var checkParentRels = [entity];
61759
61760                     if (entity.type === 'node') {
61761                         graph.parentWays(entity).forEach(function(parentWay) {
61762                             acc.add(parentWay.id); // include parent ways
61763                             checkParentRels.push(parentWay);
61764                         });
61765                     } else if (entity.type === 'relation') {
61766                         entity.members.forEach(function(member) {
61767                             acc.add(member.id); // include members
61768                         });
61769                     } else if (entity.type === 'way') {
61770                         entity.nodes.forEach(function(nodeID) {
61771                             acc.add(nodeID); // include child nodes
61772                             graph._parentWays[nodeID].forEach(function(wayID) {
61773                                 acc.add(wayID); // include connected ways
61774                             });
61775                         });
61776                     }
61777
61778                     checkParentRels.forEach(function(entity) {   // include parent relations
61779                         if (entity.type !== 'relation') {        // but not super-relations
61780                             graph.parentRelations(entity).forEach(function(parentRelation) {
61781                                 acc.add(parentRelation.id);
61782                             });
61783                         }
61784                     });
61785
61786                     return acc;
61787
61788                 }, new Set());
61789             }
61790
61791             //
61792             // Run validation for several entities, supplied `entityIDs`,
61793             // against `graph` for the given `cache`
61794             //
61795             function validateEntities(entityIDs, graph, cache) {
61796
61797                 // clear caches for existing issues related to these entities
61798                 entityIDs.forEach(cache.uncacheEntityID);
61799
61800                 // detect new issues and update caches
61801                 entityIDs.forEach(function(entityID) {
61802                     var entity = graph.hasEntity(entityID);
61803                     // don't validate deleted entities
61804                     if (!entity) { return; }
61805
61806                     var issues = validateEntity(entity, graph);
61807                     cache.cacheIssues(issues);
61808                 });
61809             }
61810
61811
61812             //
61813             // Validates anything that has changed since the last time it was run.
61814             // Also updates the "validatedGraph" to be the current graph
61815             // and dispatches a `validated` event when finished.
61816             //
61817             validator.validate = function() {
61818
61819                 var currGraph = context.graph();
61820                 _validatedGraph = _validatedGraph || context.history().base();
61821                 if (currGraph === _validatedGraph) {
61822                     dispatch$1.call('validated');
61823                     return;
61824                 }
61825                 var oldGraph = _validatedGraph;
61826                 var difference = coreDifference(oldGraph, currGraph);
61827                 _validatedGraph = currGraph;
61828
61829                 var createdAndModifiedEntityIDs = difference.extantIDs(true);   // created/modified (true = w/relation members)
61830                 var entityIDsToCheck = entityIDsToValidate(createdAndModifiedEntityIDs, currGraph);
61831
61832                 // check modified and deleted entities against the old graph in order to update their related entities
61833                 // (e.g. deleting the only highway connected to a road should create a disconnected highway issue)
61834                 var modifiedAndDeletedEntityIDs = difference.deleted().concat(difference.modified())
61835                     .map(function(entity) { return entity.id; });
61836                 var entityIDsToCheckForOldGraph = entityIDsToValidate(modifiedAndDeletedEntityIDs, oldGraph);
61837
61838                 // concat the sets
61839                 entityIDsToCheckForOldGraph.forEach(entityIDsToCheck.add, entityIDsToCheck);
61840
61841                 validateEntities(entityIDsToCheck, context.graph(), _headCache);
61842
61843                 dispatch$1.call('validated');
61844             };
61845
61846
61847             // WHEN TO RUN VALIDATION:
61848             // When graph changes:
61849             context.history()
61850                 .on('restore.validator', validator.validate)   // restore saved history
61851                 .on('undone.validator', validator.validate)    // undo
61852                 .on('redone.validator', validator.validate);   // redo
61853                 // but not on 'change' (e.g. while drawing)
61854
61855             // When user chages editing modes:
61856             context
61857                 .on('exit.validator', validator.validate);
61858
61859             // When merging fetched data:
61860             context.history()
61861                 .on('merge.validator', function(entities) {
61862                     if (!entities) { return; }
61863                     var handle = window.requestIdleCallback(function() {
61864                         var entityIDs = entities.map(function(entity) { return entity.id; });
61865                         var headGraph = context.graph();
61866                         validateEntities(entityIDsToValidate(entityIDs, headGraph), headGraph, _headCache);
61867
61868                         var baseGraph = context.history().base();
61869                         validateEntities(entityIDsToValidate(entityIDs, baseGraph), baseGraph, _baseCache);
61870
61871                         dispatch$1.call('validated');
61872                     });
61873                     _deferred.add(handle);
61874                 });
61875
61876
61877             return validator;
61878         }
61879
61880
61881         function validationCache() {
61882
61883             var cache = {
61884                 issuesByIssueID: {},  // issue.id -> issue
61885                 issuesByEntityID: {} // entity.id -> set(issue.id)
61886             };
61887
61888             cache.cacheIssues = function(issues) {
61889                 issues.forEach(function(issue) {
61890                     var entityIds = issue.entityIds || [];
61891                     entityIds.forEach(function(entityId) {
61892                         if (!cache.issuesByEntityID[entityId]) {
61893                             cache.issuesByEntityID[entityId] = new Set();
61894                         }
61895                         cache.issuesByEntityID[entityId].add(issue.id);
61896                     });
61897                     cache.issuesByIssueID[issue.id] = issue;
61898                 });
61899             };
61900
61901             cache.uncacheIssue = function(issue) {
61902                 // When multiple entities are involved (e.g. crossing_ways),
61903                 // remove this issue from the other entity caches too..
61904                 var entityIds = issue.entityIds || [];
61905                 entityIds.forEach(function(entityId) {
61906                     if (cache.issuesByEntityID[entityId]) {
61907                         cache.issuesByEntityID[entityId].delete(issue.id);
61908                     }
61909                 });
61910                 delete cache.issuesByIssueID[issue.id];
61911             };
61912
61913             cache.uncacheIssues = function(issues) {
61914                 issues.forEach(cache.uncacheIssue);
61915             };
61916
61917             cache.uncacheIssuesOfType = function(type) {
61918                 var issuesOfType = Object.values(cache.issuesByIssueID)
61919                     .filter(function(issue) { return issue.type === type; });
61920                 cache.uncacheIssues(issuesOfType);
61921             };
61922
61923             //
61924             // Remove a single entity and all its related issues from the caches
61925             //
61926             cache.uncacheEntityID = function(entityID) {
61927                 var issueIDs = cache.issuesByEntityID[entityID];
61928                 if (!issueIDs) { return; }
61929
61930                 issueIDs.forEach(function(issueID) {
61931                     var issue = cache.issuesByIssueID[issueID];
61932                     if (issue) {
61933                         cache.uncacheIssue(issue);
61934                     } else {
61935                         delete cache.issuesByIssueID[issueID];
61936                     }
61937                 });
61938
61939                 delete cache.issuesByEntityID[entityID];
61940             };
61941
61942             return cache;
61943         }
61944
61945         function coreUploader(context) {
61946
61947             var dispatch$1 = dispatch(
61948                 // Start and end events are dispatched exactly once each per legitimate outside call to `save`
61949                 'saveStarted', // dispatched as soon as a call to `save` has been deemed legitimate
61950                 'saveEnded',   // dispatched after the result event has been dispatched
61951
61952                 'willAttemptUpload', // dispatched before the actual upload call occurs, if it will
61953                 'progressChanged',
61954
61955                 // Each save results in one of these outcomes:
61956                 'resultNoChanges', // upload wasn't attempted since there were no edits
61957                 'resultErrors',    // upload failed due to errors
61958                 'resultConflicts', // upload failed due to data conflicts
61959                 'resultSuccess'    // upload completed without errors
61960             );
61961
61962             var _isSaving = false;
61963
61964             var _conflicts = [];
61965             var _errors = [];
61966             var _origChanges;
61967
61968             var _discardTags = {};
61969             _mainFileFetcher.get('discarded')
61970                 .then(function(d) { _discardTags = d; })
61971                 .catch(function() { /* ignore */ });
61972
61973             var uploader = utilRebind({}, dispatch$1, 'on');
61974
61975             uploader.isSaving = function() {
61976                 return _isSaving;
61977             };
61978
61979             uploader.save = function(changeset, tryAgain, checkConflicts) {
61980                 // Guard against accidentally entering save code twice - #4641
61981                 if (_isSaving && !tryAgain) {
61982                     return;
61983                 }
61984
61985                 var osm = context.connection();
61986                 if (!osm) { return; }
61987
61988                 // If user somehow got logged out mid-save, try to reauthenticate..
61989                 // This can happen if they were logged in from before, but the tokens are no longer valid.
61990                 if (!osm.authenticated()) {
61991                     osm.authenticate(function(err) {
61992                         if (!err) {
61993                             uploader.save(changeset, tryAgain, checkConflicts);  // continue where we left off..
61994                         }
61995                     });
61996                     return;
61997                 }
61998
61999                 if (!_isSaving) {
62000                     _isSaving = true;
62001                     dispatch$1.call('saveStarted', this);
62002                 }
62003
62004                 var history = context.history();
62005
62006                 _conflicts = [];
62007                 _errors = [];
62008
62009                 // Store original changes, in case user wants to download them as an .osc file
62010                 _origChanges = history.changes(actionDiscardTags(history.difference(), _discardTags));
62011
62012                 // First time, `history.perform` a no-op action.
62013                 // Any conflict resolutions will be done as `history.replace`
62014                 // Remember to pop this later if needed
62015                 if (!tryAgain) {
62016                     history.perform(actionNoop());
62017                 }
62018
62019                 // Attempt a fast upload.. If there are conflicts, re-enter with `checkConflicts = true`
62020                 if (!checkConflicts) {
62021                     upload(changeset);
62022
62023                 // Do the full (slow) conflict check..
62024                 } else {
62025                     performFullConflictCheck(changeset);
62026                 }
62027
62028             };
62029
62030
62031             function performFullConflictCheck(changeset) {
62032
62033                 var osm = context.connection();
62034                 if (!osm) { return; }
62035
62036                 var history = context.history();
62037
62038                 var localGraph = context.graph();
62039                 var remoteGraph = coreGraph(history.base(), true);
62040
62041                 var summary = history.difference().summary();
62042                 var _toCheck = [];
62043                 for (var i = 0; i < summary.length; i++) {
62044                     var item = summary[i];
62045                     if (item.changeType === 'modified') {
62046                         _toCheck.push(item.entity.id);
62047                     }
62048                 }
62049
62050                 var _toLoad = withChildNodes(_toCheck, localGraph);
62051                 var _loaded = {};
62052                 var _toLoadCount = 0;
62053                 var _toLoadTotal = _toLoad.length;
62054
62055                 if (_toCheck.length) {
62056                     dispatch$1.call('progressChanged', this, _toLoadCount, _toLoadTotal);
62057                     _toLoad.forEach(function(id) { _loaded[id] = false; });
62058                     osm.loadMultiple(_toLoad, loaded);
62059                 } else {
62060                     upload(changeset);
62061                 }
62062
62063                 return;
62064
62065                 function withChildNodes(ids, graph) {
62066                     var s = new Set(ids);
62067                     ids.forEach(function(id) {
62068                         var entity = graph.entity(id);
62069                         if (entity.type !== 'way') { return; }
62070
62071                         graph.childNodes(entity).forEach(function(child) {
62072                             if (child.version !== undefined) {
62073                                 s.add(child.id);
62074                             }
62075                         });
62076                     });
62077
62078                     return Array.from(s);
62079                 }
62080
62081
62082                 // Reload modified entities into an alternate graph and check for conflicts..
62083                 function loaded(err, result) {
62084                     if (_errors.length) { return; }
62085
62086                     if (err) {
62087                         _errors.push({
62088                             msg: err.message || err.responseText,
62089                             details: [ _t('save.status_code', { code: err.status }) ]
62090                         });
62091                         didResultInErrors();
62092
62093                     } else {
62094                         var loadMore = [];
62095
62096                         result.data.forEach(function(entity) {
62097                             remoteGraph.replace(entity);
62098                             _loaded[entity.id] = true;
62099                             _toLoad = _toLoad.filter(function(val) { return val !== entity.id; });
62100
62101                             if (!entity.visible) { return; }
62102
62103                             // Because loadMultiple doesn't download /full like loadEntity,
62104                             // need to also load children that aren't already being checked..
62105                             var i, id;
62106                             if (entity.type === 'way') {
62107                                 for (i = 0; i < entity.nodes.length; i++) {
62108                                     id = entity.nodes[i];
62109                                     if (_loaded[id] === undefined) {
62110                                         _loaded[id] = false;
62111                                         loadMore.push(id);
62112                                     }
62113                                 }
62114                             } else if (entity.type === 'relation' && entity.isMultipolygon()) {
62115                                 for (i = 0; i < entity.members.length; i++) {
62116                                     id = entity.members[i].id;
62117                                     if (_loaded[id] === undefined) {
62118                                         _loaded[id] = false;
62119                                         loadMore.push(id);
62120                                     }
62121                                 }
62122                             }
62123                         });
62124
62125                         _toLoadCount += result.data.length;
62126                         _toLoadTotal += loadMore.length;
62127                         dispatch$1.call('progressChanged', this, _toLoadCount, _toLoadTotal);
62128
62129                         if (loadMore.length) {
62130                             _toLoad.push.apply(_toLoad, loadMore);
62131                             osm.loadMultiple(loadMore, loaded);
62132                         }
62133
62134                         if (!_toLoad.length) {
62135                             detectConflicts();
62136                             upload(changeset);
62137                         }
62138                     }
62139                 }
62140
62141
62142                 function detectConflicts() {
62143                     function choice(id, text, action) {
62144                         return {
62145                             id: id,
62146                             text: text,
62147                             action: function() {
62148                                 history.replace(action);
62149                             }
62150                         };
62151                     }
62152                     function formatUser(d) {
62153                         return '<a href="' + osm.userURL(d) + '" target="_blank">' + d + '</a>';
62154                     }
62155                     function entityName(entity) {
62156                         return utilDisplayName(entity) || (utilDisplayType(entity.id) + ' ' + entity.id);
62157                     }
62158
62159                     function sameVersions(local, remote) {
62160                         if (local.version !== remote.version) { return false; }
62161
62162                         if (local.type === 'way') {
62163                             var children = utilArrayUnion(local.nodes, remote.nodes);
62164                             for (var i = 0; i < children.length; i++) {
62165                                 var a = localGraph.hasEntity(children[i]);
62166                                 var b = remoteGraph.hasEntity(children[i]);
62167                                 if (a && b && a.version !== b.version) { return false; }
62168                             }
62169                         }
62170
62171                         return true;
62172                     }
62173
62174                     _toCheck.forEach(function(id) {
62175                         var local = localGraph.entity(id);
62176                         var remote = remoteGraph.entity(id);
62177
62178                         if (sameVersions(local, remote)) { return; }
62179
62180                         var merge = actionMergeRemoteChanges(id, localGraph, remoteGraph, _discardTags, formatUser);
62181
62182                         history.replace(merge);
62183
62184                         var mergeConflicts = merge.conflicts();
62185                         if (!mergeConflicts.length) { return; }  // merged safely
62186
62187                         var forceLocal = actionMergeRemoteChanges(id, localGraph, remoteGraph, _discardTags).withOption('force_local');
62188                         var forceRemote = actionMergeRemoteChanges(id, localGraph, remoteGraph, _discardTags).withOption('force_remote');
62189                         var keepMine = _t('save.conflict.' + (remote.visible ? 'keep_local' : 'restore'));
62190                         var keepTheirs = _t('save.conflict.' + (remote.visible ? 'keep_remote' : 'delete'));
62191
62192                         _conflicts.push({
62193                             id: id,
62194                             name: entityName(local),
62195                             details: mergeConflicts,
62196                             chosen: 1,
62197                             choices: [
62198                                 choice(id, keepMine, forceLocal),
62199                                 choice(id, keepTheirs, forceRemote)
62200                             ]
62201                         });
62202                     });
62203                 }
62204             }
62205
62206
62207             function upload(changeset) {
62208                 var osm = context.connection();
62209                 if (!osm) {
62210                     _errors.push({ msg: 'No OSM Service' });
62211                 }
62212
62213                 if (_conflicts.length) {
62214                     didResultInConflicts(changeset);
62215
62216                 } else if (_errors.length) {
62217                     didResultInErrors();
62218
62219                 } else {
62220                     var history = context.history();
62221                     var changes = history.changes(actionDiscardTags(history.difference(), _discardTags));
62222                     if (changes.modified.length || changes.created.length || changes.deleted.length) {
62223
62224                         dispatch$1.call('willAttemptUpload', this);
62225
62226                         osm.putChangeset(changeset, changes, uploadCallback);
62227
62228                     } else {
62229                         // changes were insignificant or reverted by user
62230                         didResultInNoChanges();
62231                     }
62232                 }
62233             }
62234
62235
62236             function uploadCallback(err, changeset) {
62237                 if (err) {
62238                     if (err.status === 409) {  // 409 Conflict
62239                         uploader.save(changeset, true, true);  // tryAgain = true, checkConflicts = true
62240                     } else {
62241                         _errors.push({
62242                             msg: err.message || err.responseText,
62243                             details: [ _t('save.status_code', { code: err.status }) ]
62244                         });
62245                         didResultInErrors();
62246                     }
62247
62248                 } else {
62249                     didResultInSuccess(changeset);
62250                 }
62251             }
62252
62253             function didResultInNoChanges() {
62254
62255                 dispatch$1.call('resultNoChanges', this);
62256
62257                 endSave();
62258
62259                 context.flush(); // reset iD
62260             }
62261
62262             function didResultInErrors() {
62263
62264                 context.history().pop();
62265
62266                 dispatch$1.call('resultErrors', this, _errors);
62267
62268                 endSave();
62269             }
62270
62271
62272             function didResultInConflicts(changeset) {
62273
62274                 _conflicts.sort(function(a, b) { return b.id.localeCompare(a.id); });
62275
62276                 dispatch$1.call('resultConflicts', this, changeset, _conflicts, _origChanges);
62277
62278                 endSave();
62279             }
62280
62281
62282             function didResultInSuccess(changeset) {
62283
62284                 // delete the edit stack cached to local storage
62285                 context.history().clearSaved();
62286
62287                 dispatch$1.call('resultSuccess', this, changeset);
62288
62289                 // Add delay to allow for postgres replication #1646 #2678
62290                 window.setTimeout(function() {
62291
62292                     endSave();
62293
62294                     context.flush(); // reset iD
62295                 }, 2500);
62296             }
62297
62298
62299             function endSave() {
62300                 _isSaving = false;
62301
62302                 dispatch$1.call('saveEnded', this);
62303             }
62304
62305
62306             uploader.cancelConflictResolution = function() {
62307                 context.history().pop();
62308             };
62309
62310
62311             uploader.processResolvedConflicts = function(changeset) {
62312                 var history = context.history();
62313
62314                 for (var i = 0; i < _conflicts.length; i++) {
62315                     if (_conflicts[i].chosen === 1) {  // user chose "use theirs"
62316                         var entity = context.hasEntity(_conflicts[i].id);
62317                         if (entity && entity.type === 'way') {
62318                             var children = utilArrayUniq(entity.nodes);
62319                             for (var j = 0; j < children.length; j++) {
62320                                 history.replace(actionRevert(children[j]));
62321                             }
62322                         }
62323                         history.replace(actionRevert(_conflicts[i].id));
62324                     }
62325                 }
62326
62327                 uploader.save(changeset, true, false);  // tryAgain = true, checkConflicts = false
62328             };
62329
62330
62331             uploader.reset = function() {
62332
62333             };
62334
62335
62336             return uploader;
62337         }
62338
62339         var isRetina = window.devicePixelRatio && window.devicePixelRatio >= 2;
62340
62341         // listen for DPI change, e.g. when dragging a browser window from a retina to non-retina screen
62342         window.matchMedia("\n        (-webkit-min-device-pixel-ratio: 2), /* Safari */\n        (min-resolution: 2dppx),             /* standard */\n        (min-resolution: 192dpi)             /* fallback */\n    ").addListener(function() {
62343
62344             isRetina = window.devicePixelRatio && window.devicePixelRatio >= 2;
62345         });
62346
62347
62348         function localeDateString(s) {
62349             if (!s) { return null; }
62350             var options = { day: 'numeric', month: 'short', year: 'numeric' };
62351             var d = new Date(s);
62352             if (isNaN(d.getTime())) { return null; }
62353             return d.toLocaleDateString(_mainLocalizer.localeCode(), options);
62354         }
62355
62356         function vintageRange(vintage) {
62357             var s;
62358             if (vintage.start || vintage.end) {
62359                 s = (vintage.start || '?');
62360                 if (vintage.start !== vintage.end) {
62361                     s += ' - ' + (vintage.end || '?');
62362                 }
62363             }
62364             return s;
62365         }
62366
62367
62368         function rendererBackgroundSource(data) {
62369             var source = Object.assign({}, data);   // shallow copy
62370             var _offset = [0, 0];
62371             var _name = source.name;
62372             var _description = source.description;
62373             var _best = !!source.best;
62374             var _template = source.encrypted ? utilAesDecrypt(source.template) : source.template;
62375
62376             source.tileSize = data.tileSize || 256;
62377             source.zoomExtent = data.zoomExtent || [0, 22];
62378             source.overzoom = data.overzoom !== false;
62379
62380             source.offset = function(val) {
62381                 if (!arguments.length) { return _offset; }
62382                 _offset = val;
62383                 return source;
62384             };
62385
62386
62387             source.nudge = function(val, zoomlevel) {
62388                 _offset[0] += val[0] / Math.pow(2, zoomlevel);
62389                 _offset[1] += val[1] / Math.pow(2, zoomlevel);
62390                 return source;
62391             };
62392
62393
62394             source.name = function() {
62395                 var id_safe = source.id.replace(/\./g, '<TX_DOT>');
62396                 return _t('imagery.' + id_safe + '.name', { default: _name });
62397             };
62398
62399
62400             source.description = function() {
62401                 var id_safe = source.id.replace(/\./g, '<TX_DOT>');
62402                 return _t('imagery.' + id_safe + '.description', { default: _description });
62403             };
62404
62405
62406             source.best = function() {
62407                 return _best;
62408             };
62409
62410
62411             source.area = function() {
62412                 if (!data.polygon) { return Number.MAX_VALUE; }  // worldwide
62413                 var area = d3_geoArea({ type: 'MultiPolygon', coordinates: [ data.polygon ] });
62414                 return isNaN(area) ? 0 : area;
62415             };
62416
62417
62418             source.imageryUsed = function() {
62419                 return name || source.id;
62420             };
62421
62422
62423             source.template = function(val) {
62424                 if (!arguments.length) { return _template; }
62425                 if (source.id === 'custom') {
62426                     _template = val;
62427                 }
62428                 return source;
62429             };
62430
62431
62432             source.url = function(coord) {
62433                 var result = _template;
62434                 if (result === '') { return result; }   // source 'none'
62435
62436
62437                 // Guess a type based on the tokens present in the template
62438                 // (This is for 'custom' source, where we don't know)
62439                 if (!source.type) {
62440                     if (/\{(proj|wkid|bbox)\}/.test(_template)) {
62441                         source.type = 'wms';
62442                         source.projection = 'EPSG:3857';  // guess
62443                     } else if (/\{(x|y)\}/.test(_template)) {
62444                         source.type = 'tms';
62445                     } else if (/\{u\}/.test(_template)) {
62446                         source.type = 'bing';
62447                     }
62448                 }
62449
62450
62451                 if (source.type === 'wms') {
62452                     var tileToProjectedCoords = (function(x, y, z) {
62453                         //polyfill for IE11, PhantomJS
62454                         var sinh = Math.sinh || function(x) {
62455                             var y = Math.exp(x);
62456                             return (y - 1 / y) / 2;
62457                         };
62458
62459                         var zoomSize = Math.pow(2, z);
62460                         var lon = x / zoomSize * Math.PI * 2 - Math.PI;
62461                         var lat = Math.atan(sinh(Math.PI * (1 - 2 * y / zoomSize)));
62462
62463                         switch (source.projection) {
62464                             case 'EPSG:4326':
62465                                 return {
62466                                     x: lon * 180 / Math.PI,
62467                                     y: lat * 180 / Math.PI
62468                                 };
62469                             default: // EPSG:3857 and synonyms
62470                                 var mercCoords = mercatorRaw(lon, lat);
62471                                 return {
62472                                     x: 20037508.34 / Math.PI * mercCoords[0],
62473                                     y: 20037508.34 / Math.PI * mercCoords[1]
62474                                 };
62475                         }
62476                     });
62477
62478                     var tileSize = source.tileSize;
62479                     var projection = source.projection;
62480                     var minXmaxY = tileToProjectedCoords(coord[0], coord[1], coord[2]);
62481                     var maxXminY = tileToProjectedCoords(coord[0]+1, coord[1]+1, coord[2]);
62482
62483                     result = result.replace(/\{(\w+)\}/g, function (token, key) {
62484                       switch (key) {
62485                         case 'width':
62486                         case 'height':
62487                           return tileSize;
62488                         case 'proj':
62489                           return projection;
62490                         case 'wkid':
62491                           return projection.replace(/^EPSG:/, '');
62492                         case 'bbox':
62493                           return minXmaxY.x + ',' + maxXminY.y + ',' + maxXminY.x + ',' + minXmaxY.y;
62494                         case 'w':
62495                           return minXmaxY.x;
62496                         case 's':
62497                           return maxXminY.y;
62498                         case 'n':
62499                           return maxXminY.x;
62500                         case 'e':
62501                           return minXmaxY.y;
62502                         default:
62503                           return token;
62504                       }
62505                     });
62506
62507                 } else if (source.type === 'tms') {
62508                     result = result
62509                         .replace('{x}', coord[0])
62510                         .replace('{y}', coord[1])
62511                         // TMS-flipped y coordinate
62512                         .replace(/\{[t-]y\}/, Math.pow(2, coord[2]) - coord[1] - 1)
62513                         .replace(/\{z(oom)?\}/, coord[2])
62514                         // only fetch retina tiles for retina screens
62515                         .replace(/\{@2x\}|\{r\}/, isRetina ? '@2x' : '');
62516
62517                 } else if (source.type === 'bing') {
62518                     result = result
62519                         .replace('{u}', function() {
62520                             var u = '';
62521                             for (var zoom = coord[2]; zoom > 0; zoom--) {
62522                                 var b = 0;
62523                                 var mask = 1 << (zoom - 1);
62524                                 if ((coord[0] & mask) !== 0) { b++; }
62525                                 if ((coord[1] & mask) !== 0) { b += 2; }
62526                                 u += b.toString();
62527                             }
62528                             return u;
62529                         });
62530                 }
62531
62532                 // these apply to any type..
62533                 result = result.replace(/\{switch:([^}]+)\}/, function(s, r) {
62534                     var subdomains = r.split(',');
62535                     return subdomains[(coord[0] + coord[1]) % subdomains.length];
62536                 });
62537
62538
62539                 return result;
62540             };
62541
62542
62543             source.validZoom = function(z) {
62544                 return source.zoomExtent[0] <= z &&
62545                     (source.overzoom || source.zoomExtent[1] > z);
62546             };
62547
62548
62549             source.isLocatorOverlay = function() {
62550                 return source.id === 'mapbox_locator_overlay';
62551             };
62552
62553
62554             /* hides a source from the list, but leaves it available for use */
62555             source.isHidden = function() {
62556                 return source.id === 'DigitalGlobe-Premium-vintage' ||
62557                     source.id === 'DigitalGlobe-Standard-vintage';
62558             };
62559
62560
62561             source.copyrightNotices = function() {};
62562
62563
62564             source.getMetadata = function(center, tileCoord, callback) {
62565                 var vintage = {
62566                     start: localeDateString(source.startDate),
62567                     end: localeDateString(source.endDate)
62568                 };
62569                 vintage.range = vintageRange(vintage);
62570
62571                 var metadata = { vintage: vintage };
62572                 callback(null, metadata);
62573             };
62574
62575
62576             return source;
62577         }
62578
62579
62580         rendererBackgroundSource.Bing = function(data, dispatch) {
62581             // http://msdn.microsoft.com/en-us/library/ff701716.aspx
62582             // http://msdn.microsoft.com/en-us/library/ff701701.aspx
62583
62584             data.template = 'https://ecn.t{switch:0,1,2,3}.tiles.virtualearth.net/tiles/a{u}.jpeg?g=587&mkt=en-gb&n=z';
62585
62586             var bing = rendererBackgroundSource(data);
62587             // var key = 'Arzdiw4nlOJzRwOz__qailc8NiR31Tt51dN2D7cm57NrnceZnCpgOkmJhNpGoppU'; // P2, JOSM, etc
62588             var key = 'Ak5oTE46TUbjRp08OFVcGpkARErDobfpuyNKa-W2mQ8wbt1K1KL8p1bIRwWwcF-Q';    // iD
62589
62590
62591             var url = 'https://dev.virtualearth.net/REST/v1/Imagery/Metadata/Aerial?include=ImageryProviders&key=' + key;
62592             var cache = {};
62593             var inflight = {};
62594             var providers = [];
62595
62596             d3_json(url)
62597                 .then(function(json) {
62598                     providers = json.resourceSets[0].resources[0].imageryProviders.map(function(provider) {
62599                         return {
62600                             attribution: provider.attribution,
62601                             areas: provider.coverageAreas.map(function(area) {
62602                                 return {
62603                                     zoom: [area.zoomMin, area.zoomMax],
62604                                     extent: geoExtent([area.bbox[1], area.bbox[0]], [area.bbox[3], area.bbox[2]])
62605                                 };
62606                             })
62607                         };
62608                     });
62609                     dispatch.call('change');
62610                 })
62611                 .catch(function() {
62612                     /* ignore */
62613                 });
62614
62615
62616             bing.copyrightNotices = function(zoom, extent) {
62617                 zoom = Math.min(zoom, 21);
62618                 return providers.filter(function(provider) {
62619                     return provider.areas.some(function(area) {
62620                         return extent.intersects(area.extent) &&
62621                             area.zoom[0] <= zoom &&
62622                             area.zoom[1] >= zoom;
62623                     });
62624                 }).map(function(provider) {
62625                     return provider.attribution;
62626                 }).join(', ');
62627             };
62628
62629
62630             bing.getMetadata = function(center, tileCoord, callback) {
62631                 var tileID = tileCoord.slice(0, 3).join('/');
62632                 var zoom = Math.min(tileCoord[2], 21);
62633                 var centerPoint = center[1] + ',' + center[0];  // lat,lng
62634                 var url = 'https://dev.virtualearth.net/REST/v1/Imagery/Metadata/Aerial/' + centerPoint +
62635                         '?zl=' + zoom + '&key=' + key;
62636
62637                 if (inflight[tileID]) { return; }
62638
62639                 if (!cache[tileID]) {
62640                     cache[tileID] = {};
62641                 }
62642                 if (cache[tileID] && cache[tileID].metadata) {
62643                     return callback(null, cache[tileID].metadata);
62644                 }
62645
62646                 inflight[tileID] = true;
62647                 d3_json(url)
62648                     .then(function(result) {
62649                         delete inflight[tileID];
62650                         if (!result) {
62651                             throw new Error('Unknown Error');
62652                         }
62653                         var vintage = {
62654                             start: localeDateString(result.resourceSets[0].resources[0].vintageStart),
62655                             end: localeDateString(result.resourceSets[0].resources[0].vintageEnd)
62656                         };
62657                         vintage.range = vintageRange(vintage);
62658
62659                         var metadata = { vintage: vintage };
62660                         cache[tileID].metadata = metadata;
62661                         if (callback) { callback(null, metadata); }
62662                     })
62663                     .catch(function(err) {
62664                         delete inflight[tileID];
62665                         if (callback) { callback(err.message); }
62666                     });
62667             };
62668
62669
62670             bing.terms_url = 'https://blog.openstreetmap.org/2010/11/30/microsoft-imagery-details';
62671
62672
62673             return bing;
62674         };
62675
62676
62677
62678         rendererBackgroundSource.Esri = function(data) {
62679             // in addition to using the tilemap at zoom level 20, overzoom real tiles - #4327 (deprecated technique, but it works)
62680             if (data.template.match(/blankTile/) === null) {
62681                 data.template = data.template + '?blankTile=false';
62682             }
62683
62684             var esri = rendererBackgroundSource(data);
62685             var cache = {};
62686             var inflight = {};
62687             var _prevCenter;
62688
62689             // use a tilemap service to set maximum zoom for esri tiles dynamically
62690             // https://developers.arcgis.com/documentation/tiled-elevation-service/
62691             esri.fetchTilemap = function(center) {
62692                 // skip if we have already fetched a tilemap within 5km
62693                 if (_prevCenter && geoSphericalDistance(center, _prevCenter) < 5000) { return; }
62694                 _prevCenter = center;
62695
62696                 // tiles are available globally to zoom level 19, afterward they may or may not be present
62697                 var z = 20;
62698
62699                 // first generate a random url using the template
62700                 var dummyUrl = esri.url([1,2,3]);
62701
62702                 // calculate url z/y/x from the lat/long of the center of the map
62703                 var x = (Math.floor((center[0] + 180) / 360 * Math.pow(2, z)));
62704                 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)));
62705
62706                 // fetch an 8x8 grid to leverage cache
62707                 var tilemapUrl = dummyUrl.replace(/tile\/[0-9]+\/[0-9]+\/[0-9]+\?blankTile=false/, 'tilemap') + '/' + z + '/' + y + '/' + x + '/8/8';
62708
62709                 // make the request and introspect the response from the tilemap server
62710                 d3_json(tilemapUrl)
62711                     .then(function(tilemap) {
62712                         if (!tilemap) {
62713                             throw new Error('Unknown Error');
62714                         }
62715                         var hasTiles = true;
62716                         for (var i = 0; i < tilemap.data.length; i++) {
62717                             // 0 means an individual tile in the grid doesn't exist
62718                             if (!tilemap.data[i]) {
62719                                 hasTiles = false;
62720                                 break;
62721                             }
62722                         }
62723
62724                         // if any tiles are missing at level 20 we restrict maxZoom to 19
62725                         esri.zoomExtent[1] = (hasTiles ? 22 : 19);
62726                     })
62727                     .catch(function() {
62728                         /* ignore */
62729                     });
62730             };
62731
62732
62733             esri.getMetadata = function(center, tileCoord, callback) {
62734                 var tileID = tileCoord.slice(0, 3).join('/');
62735                 var zoom = Math.min(tileCoord[2], esri.zoomExtent[1]);
62736                 var centerPoint = center[0] + ',' + center[1];  // long, lat (as it should be)
62737                 var unknown = _t('info_panels.background.unknown');
62738                 var metadataLayer;
62739                 var vintage = {};
62740                 var metadata = {};
62741
62742                 if (inflight[tileID]) { return; }
62743
62744                 switch (true) {
62745                     case (zoom >= 20 && esri.id === 'EsriWorldImageryClarity'):
62746                         metadataLayer = 4;
62747                         break;
62748                     case zoom >= 19:
62749                         metadataLayer = 3;
62750                         break;
62751                     case zoom >= 17:
62752                         metadataLayer = 2;
62753                         break;
62754                     case zoom >= 13:
62755                         metadataLayer = 0;
62756                         break;
62757                     default:
62758                         metadataLayer = 99;
62759                 }
62760
62761                 var url;
62762                 // build up query using the layer appropriate to the current zoom
62763                 if (esri.id === 'EsriWorldImagery') {
62764                     url = 'https://services.arcgisonline.com/arcgis/rest/services/World_Imagery/MapServer/';
62765                 } else if (esri.id === 'EsriWorldImageryClarity') {
62766                     url = 'https://serviceslab.arcgisonline.com/arcgis/rest/services/Clarity_World_Imagery/MapServer/';
62767                 }
62768
62769                 url += metadataLayer + '/query?returnGeometry=false&geometry=' + centerPoint + '&inSR=4326&geometryType=esriGeometryPoint&outFields=*&f=json';
62770
62771                 if (!cache[tileID]) {
62772                     cache[tileID] = {};
62773                 }
62774                 if (cache[tileID] && cache[tileID].metadata) {
62775                     return callback(null, cache[tileID].metadata);
62776                 }
62777
62778                 // accurate metadata is only available >= 13
62779                 if (metadataLayer === 99) {
62780                     vintage = {
62781                         start: null,
62782                         end: null,
62783                         range: null
62784                     };
62785                     metadata = {
62786                         vintage: null,
62787                         source: unknown,
62788                         description: unknown,
62789                         resolution: unknown,
62790                         accuracy: unknown
62791                     };
62792
62793                     callback(null, metadata);
62794
62795                 } else {
62796                     inflight[tileID] = true;
62797                     d3_json(url)
62798                         .then(function(result) {
62799                             delete inflight[tileID];
62800                             if (!result) {
62801                                 throw new Error('Unknown Error');
62802                             } else if (result.features && result.features.length < 1) {
62803                                 throw new Error('No Results');
62804                             } else if (result.error && result.error.message) {
62805                                 throw new Error(result.error.message);
62806                             }
62807
62808                             // pass through the discrete capture date from metadata
62809                             var captureDate = localeDateString(result.features[0].attributes.SRC_DATE2);
62810                             vintage = {
62811                                 start: captureDate,
62812                                 end: captureDate,
62813                                 range: captureDate
62814                             };
62815                             metadata = {
62816                                 vintage: vintage,
62817                                 source: clean(result.features[0].attributes.NICE_NAME),
62818                                 description: clean(result.features[0].attributes.NICE_DESC),
62819                                 resolution: clean(+parseFloat(result.features[0].attributes.SRC_RES).toFixed(4)),
62820                                 accuracy: clean(+parseFloat(result.features[0].attributes.SRC_ACC).toFixed(4))
62821                             };
62822
62823                             // append units - meters
62824                             if (isFinite(metadata.resolution)) {
62825                                 metadata.resolution += ' m';
62826                             }
62827                             if (isFinite(metadata.accuracy)) {
62828                                 metadata.accuracy += ' m';
62829                             }
62830
62831                             cache[tileID].metadata = metadata;
62832                             if (callback) { callback(null, metadata); }
62833                         })
62834                         .catch(function(err) {
62835                             delete inflight[tileID];
62836                             if (callback) { callback(err.message); }
62837                         });
62838                 }
62839
62840
62841                 function clean(val) {
62842                     return String(val).trim() || unknown;
62843                 }
62844             };
62845
62846             return esri;
62847         };
62848
62849
62850         rendererBackgroundSource.None = function() {
62851             var source = rendererBackgroundSource({ id: 'none', template: '' });
62852
62853
62854             source.name = function() {
62855                 return _t('background.none');
62856             };
62857
62858
62859             source.imageryUsed = function() {
62860                 return null;
62861             };
62862
62863
62864             source.area = function() {
62865                 return -1;  // sources in background pane are sorted by area
62866             };
62867
62868
62869             return source;
62870         };
62871
62872
62873         rendererBackgroundSource.Custom = function(template) {
62874             var source = rendererBackgroundSource({ id: 'custom', template: template });
62875
62876
62877             source.name = function() {
62878                 return _t('background.custom');
62879             };
62880
62881
62882             source.imageryUsed = function() {
62883                 // sanitize personal connection tokens - #6801
62884                 var cleaned = source.template();
62885
62886                 // from query string parameters
62887                 if (cleaned.indexOf('?') !== -1) {
62888                     var parts = cleaned.split('?', 2);
62889                     var qs = utilStringQs(parts[1]);
62890
62891                     ['access_token', 'connectId', 'token'].forEach(function(param) {
62892                         if (qs[param]) {
62893                             qs[param] = '{apikey}';
62894                         }
62895                     });
62896                     cleaned = parts[0] + '?' + utilQsString(qs, true);  // true = soft encode
62897                 }
62898
62899                 // from wms/wmts api path parameters
62900                 cleaned = cleaned.replace(/token\/(\w+)/, 'token/{apikey}');
62901
62902                 return 'Custom (' + cleaned + ' )';
62903             };
62904
62905
62906             source.area = function() {
62907                 return -2;  // sources in background pane are sorted by area
62908             };
62909
62910
62911             return source;
62912         };
62913
62914         function rendererTileLayer(context) {
62915             var transformProp = utilPrefixCSSProperty('Transform');
62916             var tiler = utilTiler();
62917
62918             var _tileSize = 256;
62919             var _projection;
62920             var _cache = {};
62921             var _tileOrigin;
62922             var _zoom;
62923             var _source;
62924
62925
62926             function tileSizeAtZoom(d, z) {
62927                 var EPSILON = 0.002;    // close seams
62928                 return ((_tileSize * Math.pow(2, z - d[2])) / _tileSize) + EPSILON;
62929             }
62930
62931
62932             function atZoom(t, distance) {
62933                 var power = Math.pow(2, distance);
62934                 return [
62935                     Math.floor(t[0] * power),
62936                     Math.floor(t[1] * power),
62937                     t[2] + distance
62938                 ];
62939             }
62940
62941
62942             function lookUp(d) {
62943                 for (var up = -1; up > -d[2]; up--) {
62944                     var tile = atZoom(d, up);
62945                     if (_cache[_source.url(tile)] !== false) {
62946                         return tile;
62947                     }
62948                 }
62949             }
62950
62951
62952             function uniqueBy(a, n) {
62953                 var o = [];
62954                 var seen = {};
62955                 for (var i = 0; i < a.length; i++) {
62956                     if (seen[a[i][n]] === undefined) {
62957                         o.push(a[i]);
62958                         seen[a[i][n]] = true;
62959                     }
62960                 }
62961                 return o;
62962             }
62963
62964
62965             function addSource(d) {
62966                 d.push(_source.url(d));
62967                 return d;
62968             }
62969
62970
62971             // Update tiles based on current state of `projection`.
62972             function background(selection) {
62973                 _zoom = geoScaleToZoom(_projection.scale(), _tileSize);
62974
62975                 var pixelOffset;
62976                 if (_source) {
62977                     pixelOffset = [
62978                         _source.offset()[0] * Math.pow(2, _zoom),
62979                         _source.offset()[1] * Math.pow(2, _zoom)
62980                     ];
62981                 } else {
62982                     pixelOffset = [0, 0];
62983                 }
62984
62985                 var translate = [
62986                     _projection.translate()[0] + pixelOffset[0],
62987                     _projection.translate()[1] + pixelOffset[1]
62988                 ];
62989
62990                 tiler
62991                     .scale(_projection.scale() * 2 * Math.PI)
62992                     .translate(translate);
62993
62994                 _tileOrigin = [
62995                     _projection.scale() * Math.PI - translate[0],
62996                     _projection.scale() * Math.PI - translate[1]
62997                 ];
62998
62999                 render(selection);
63000             }
63001
63002
63003             // Derive the tiles onscreen, remove those offscreen and position them.
63004             // Important that this part not depend on `_projection` because it's
63005             // rentered when tiles load/error (see #644).
63006             function render(selection) {
63007                 if (!_source) { return; }
63008                 var requests = [];
63009                 var showDebug = context.getDebug('tile') && !_source.overlay;
63010
63011                 if (_source.validZoom(_zoom)) {
63012                     tiler.skipNullIsland(!!_source.overlay);
63013
63014                     tiler().forEach(function(d) {
63015                         addSource(d);
63016                         if (d[3] === '') { return; }
63017                         if (typeof d[3] !== 'string') { return; } // Workaround for #2295
63018                         requests.push(d);
63019                         if (_cache[d[3]] === false && lookUp(d)) {
63020                             requests.push(addSource(lookUp(d)));
63021                         }
63022                     });
63023
63024                     requests = uniqueBy(requests, 3).filter(function(r) {
63025                         // don't re-request tiles which have failed in the past
63026                         return _cache[r[3]] !== false;
63027                     });
63028                 }
63029
63030                 function load(d) {
63031                     _cache[d[3]] = true;
63032                     select(this)
63033                         .on('error', null)
63034                         .on('load', null)
63035                         .classed('tile-loaded', true);
63036                     render(selection);
63037                 }
63038
63039                 function error(d) {
63040                     _cache[d[3]] = false;
63041                     select(this)
63042                         .on('error', null)
63043                         .on('load', null)
63044                         .remove();
63045                     render(selection);
63046                 }
63047
63048                 function imageTransform(d) {
63049                     var ts = _tileSize * Math.pow(2, _zoom - d[2]);
63050                     var scale = tileSizeAtZoom(d, _zoom);
63051                     return 'translate(' +
63052                         ((d[0] * ts) - _tileOrigin[0]) + 'px,' +
63053                         ((d[1] * ts) - _tileOrigin[1]) + 'px) ' +
63054                         'scale(' + scale + ',' + scale + ')';
63055                 }
63056
63057                 function tileCenter(d) {
63058                     var ts = _tileSize * Math.pow(2, _zoom - d[2]);
63059                     return [
63060                         ((d[0] * ts) - _tileOrigin[0] + (ts / 2)),
63061                         ((d[1] * ts) - _tileOrigin[1] + (ts / 2))
63062                     ];
63063                 }
63064
63065                 function debugTransform(d) {
63066                     var coord = tileCenter(d);
63067                     return 'translate(' + coord[0] + 'px,' + coord[1] + 'px)';
63068                 }
63069
63070
63071                 // Pick a representative tile near the center of the viewport
63072                 // (This is useful for sampling the imagery vintage)
63073                 var dims = tiler.size();
63074                 var mapCenter = [dims[0] / 2, dims[1] / 2];
63075                 var minDist = Math.max(dims[0], dims[1]);
63076                 var nearCenter;
63077
63078                 requests.forEach(function(d) {
63079                     var c = tileCenter(d);
63080                     var dist = geoVecLength(c, mapCenter);
63081                     if (dist < minDist) {
63082                         minDist = dist;
63083                         nearCenter = d;
63084                     }
63085                 });
63086
63087
63088                 var image = selection.selectAll('img')
63089                     .data(requests, function(d) { return d[3]; });
63090
63091                 image.exit()
63092                     .style(transformProp, imageTransform)
63093                     .classed('tile-removing', true)
63094                     .classed('tile-center', false)
63095                     .each(function() {
63096                         var tile = select(this);
63097                         window.setTimeout(function() {
63098                             if (tile.classed('tile-removing')) {
63099                                 tile.remove();
63100                             }
63101                         }, 300);
63102                     });
63103
63104                 image.enter()
63105                   .append('img')
63106                     .attr('class', 'tile')
63107                     .attr('draggable', 'false')
63108                     .style('width', _tileSize + 'px')
63109                     .style('height', _tileSize + 'px')
63110                     .attr('src', function(d) { return d[3]; })
63111                     .on('error', error)
63112                     .on('load', load)
63113                   .merge(image)
63114                     .style(transformProp, imageTransform)
63115                     .classed('tile-debug', showDebug)
63116                     .classed('tile-removing', false)
63117                     .classed('tile-center', function(d) { return d === nearCenter; });
63118
63119
63120
63121                 var debug = selection.selectAll('.tile-label-debug')
63122                     .data(showDebug ? requests : [], function(d) { return d[3]; });
63123
63124                 debug.exit()
63125                     .remove();
63126
63127                 if (showDebug) {
63128                     var debugEnter = debug.enter()
63129                         .append('div')
63130                         .attr('class', 'tile-label-debug');
63131
63132                     debugEnter
63133                         .append('div')
63134                         .attr('class', 'tile-label-debug-coord');
63135
63136                     debugEnter
63137                         .append('div')
63138                         .attr('class', 'tile-label-debug-vintage');
63139
63140                     debug = debug.merge(debugEnter);
63141
63142                     debug
63143                         .style(transformProp, debugTransform);
63144
63145                     debug
63146                         .selectAll('.tile-label-debug-coord')
63147                         .text(function(d) { return d[2] + ' / ' + d[0] + ' / ' + d[1]; });
63148
63149                     debug
63150                         .selectAll('.tile-label-debug-vintage')
63151                         .each(function(d) {
63152                             var span = select(this);
63153                             var center = context.projection.invert(tileCenter(d));
63154                             _source.getMetadata(center, d, function(err, result) {
63155                                 span.text((result && result.vintage && result.vintage.range) ||
63156                                     _t('info_panels.background.vintage') + ': ' + _t('info_panels.background.unknown')
63157                                 );
63158                             });
63159                         });
63160                 }
63161
63162             }
63163
63164
63165             background.projection = function(val) {
63166                 if (!arguments.length) { return _projection; }
63167                 _projection = val;
63168                 return background;
63169             };
63170
63171
63172             background.dimensions = function(val) {
63173                 if (!arguments.length) { return tiler.size(); }
63174                 tiler.size(val);
63175                 return background;
63176             };
63177
63178
63179             background.source = function(val) {
63180                 if (!arguments.length) { return _source; }
63181                 _source = val;
63182                 _tileSize = _source.tileSize;
63183                 _cache = {};
63184                 tiler.tileSize(_source.tileSize).zoomExtent(_source.zoomExtent);
63185                 return background;
63186             };
63187
63188
63189             return background;
63190         }
63191
63192         var _imageryIndex = null;
63193
63194         function rendererBackground(context) {
63195           var dispatch$1 = dispatch('change');
63196           var detected = utilDetect();
63197           var baseLayer = rendererTileLayer(context).projection(context.projection);
63198           var _isValid = true;
63199           var _overlayLayers = [];
63200           var _brightness = 1;
63201           var _contrast = 1;
63202           var _saturation = 1;
63203           var _sharpness = 1;
63204
63205
63206           function ensureImageryIndex() {
63207             return _mainFileFetcher.get('imagery')
63208               .then(function (sources) {
63209                 if (_imageryIndex) { return _imageryIndex; }
63210
63211                 _imageryIndex = {
63212                   imagery: sources,
63213                   features: {}
63214                 };
63215
63216                 // use which-polygon to support efficient index and querying for imagery
63217                 var features = sources.map(function (source) {
63218                   if (!source.polygon) { return null; }
63219                   // workaround for editor-layer-index weirdness..
63220                   // Add an extra array nest to each element in `source.polygon`
63221                   // so the rings are not treated as a bunch of holes:
63222                   // what we have: [ [[outer],[hole],[hole]] ]
63223                   // what we want: [ [[outer]],[[outer]],[[outer]] ]
63224                   var rings = source.polygon.map(function (ring) { return [ring]; });
63225
63226                   var feature = {
63227                     type: 'Feature',
63228                     properties: { id: source.id },
63229                     geometry: { type: 'MultiPolygon', coordinates: rings }
63230                   };
63231
63232                   _imageryIndex.features[source.id] = feature;
63233                   return feature;
63234
63235                 }).filter(Boolean);
63236
63237                 _imageryIndex.query = whichPolygon_1({ type: 'FeatureCollection', features: features });
63238
63239
63240                 // Instantiate `rendererBackgroundSource` objects for each source
63241                 _imageryIndex.backgrounds = sources.map(function (source) {
63242                   if (source.type === 'bing') {
63243                     return rendererBackgroundSource.Bing(source, dispatch$1);
63244                   } else if (/^EsriWorldImagery/.test(source.id)) {
63245                     return rendererBackgroundSource.Esri(source);
63246                   } else {
63247                     return rendererBackgroundSource(source);
63248                   }
63249                 });
63250
63251                 // Add 'None'
63252                 _imageryIndex.backgrounds.unshift(rendererBackgroundSource.None());
63253
63254                 // Add 'Custom'
63255                 var template = corePreferences('background-custom-template') || '';
63256                 var custom = rendererBackgroundSource.Custom(template);
63257                 _imageryIndex.backgrounds.unshift(custom);
63258
63259                 return _imageryIndex;
63260               });
63261           }
63262
63263
63264           function background(selection) {
63265             var currSource = baseLayer.source();
63266
63267             // If we are displaying an Esri basemap at high zoom,
63268             // check its tilemap to see how high the zoom can go
63269             if (context.map().zoom() > 18) {
63270               if (currSource && /^EsriWorldImagery/.test(currSource.id)) {
63271                 var center = context.map().center();
63272                 currSource.fetchTilemap(center);
63273               }
63274             }
63275
63276             // Is the imagery valid here? - #4827
63277             var sources = background.sources(context.map().extent());
63278             var wasValid = _isValid;
63279             _isValid = !!sources.filter(function (d) { return d === currSource; }).length;
63280
63281             if (wasValid !== _isValid) {      // change in valid status
63282               background.updateImagery();
63283             }
63284
63285
63286             var baseFilter = '';
63287             if (detected.cssfilters) {
63288               if (_brightness !== 1) {
63289                 baseFilter += " brightness(" + _brightness + ")";
63290               }
63291               if (_contrast !== 1) {
63292                 baseFilter += " contrast(" + _contrast + ")";
63293               }
63294               if (_saturation !== 1) {
63295                 baseFilter += " saturate(" + _saturation + ")";
63296               }
63297               if (_sharpness < 1) {  // gaussian blur
63298                 var blur = d3_interpolateNumber(0.5, 5)(1 - _sharpness);
63299                 baseFilter += " blur(" + blur + "px)";
63300               }
63301             }
63302
63303             var base = selection.selectAll('.layer-background')
63304               .data([0]);
63305
63306             base = base.enter()
63307               .insert('div', '.layer-data')
63308               .attr('class', 'layer layer-background')
63309               .merge(base);
63310
63311             if (detected.cssfilters) {
63312               base.style('filter', baseFilter || null);
63313             } else {
63314               base.style('opacity', _brightness);
63315             }
63316
63317
63318             var imagery = base.selectAll('.layer-imagery')
63319               .data([0]);
63320
63321             imagery.enter()
63322               .append('div')
63323               .attr('class', 'layer layer-imagery')
63324               .merge(imagery)
63325               .call(baseLayer);
63326
63327
63328             var maskFilter = '';
63329             var mixBlendMode = '';
63330             if (detected.cssfilters && _sharpness > 1) {  // apply unsharp mask
63331               mixBlendMode = 'overlay';
63332               maskFilter = 'saturate(0) blur(3px) invert(1)';
63333
63334               var contrast = _sharpness - 1;
63335               maskFilter += " contrast(" + contrast + ")";
63336
63337               var brightness = d3_interpolateNumber(1, 0.85)(_sharpness - 1);
63338               maskFilter += " brightness(" + brightness + ")";
63339             }
63340
63341             var mask = base.selectAll('.layer-unsharp-mask')
63342               .data(detected.cssfilters && _sharpness > 1 ? [0] : []);
63343
63344             mask.exit()
63345               .remove();
63346
63347             mask.enter()
63348               .append('div')
63349               .attr('class', 'layer layer-mask layer-unsharp-mask')
63350               .merge(mask)
63351               .call(baseLayer)
63352               .style('filter', maskFilter || null)
63353               .style('mix-blend-mode', mixBlendMode || null);
63354
63355
63356             var overlays = selection.selectAll('.layer-overlay')
63357               .data(_overlayLayers, function (d) { return d.source().name(); });
63358
63359             overlays.exit()
63360               .remove();
63361
63362             overlays.enter()
63363               .insert('div', '.layer-data')
63364               .attr('class', 'layer layer-overlay')
63365               .merge(overlays)
63366               .each(function (layer, i, nodes) { return select(nodes[i]).call(layer); });
63367           }
63368
63369
63370           background.updateImagery = function() {
63371             var currSource = baseLayer.source();
63372             if (context.inIntro() || !currSource) { return; }
63373
63374             var o = _overlayLayers
63375               .filter(function (d) { return !d.source().isLocatorOverlay() && !d.source().isHidden(); })
63376               .map(function (d) { return d.source().id; })
63377               .join(',');
63378
63379             var meters = geoOffsetToMeters(currSource.offset());
63380             var EPSILON = 0.01;
63381             var x = +meters[0].toFixed(2);
63382             var y = +meters[1].toFixed(2);
63383             var hash = utilStringQs(window.location.hash);
63384
63385             var id = currSource.id;
63386             if (id === 'custom') {
63387               id = "custom:" + (currSource.template());
63388             }
63389
63390             if (id) {
63391               hash.background = id;
63392             } else {
63393               delete hash.background;
63394             }
63395
63396             if (o) {
63397               hash.overlays = o;
63398             } else {
63399               delete hash.overlays;
63400             }
63401
63402             if (Math.abs(x) > EPSILON || Math.abs(y) > EPSILON) {
63403               hash.offset = x + "," + y;
63404             } else {
63405               delete hash.offset;
63406             }
63407
63408             if (!window.mocha) {
63409               window.location.replace('#' + utilQsString(hash, true));
63410             }
63411
63412             var imageryUsed = [];
63413             var photoOverlaysUsed = [];
63414
63415             var currUsed = currSource.imageryUsed();
63416             if (currUsed && _isValid) {
63417               imageryUsed.push(currUsed);
63418             }
63419
63420             _overlayLayers
63421               .filter(function (d) { return !d.source().isLocatorOverlay() && !d.source().isHidden(); })
63422               .forEach(function (d) { return imageryUsed.push(d.source().imageryUsed()); });
63423
63424             var dataLayer = context.layers().layer('data');
63425             if (dataLayer && dataLayer.enabled() && dataLayer.hasData()) {
63426               imageryUsed.push(dataLayer.getSrc());
63427             }
63428
63429             var photoOverlayLayers = {
63430               streetside: 'Bing Streetside',
63431               mapillary: 'Mapillary Images',
63432               'mapillary-map-features': 'Mapillary Map Features',
63433               'mapillary-signs': 'Mapillary Signs',
63434               openstreetcam: 'OpenStreetCam Images'
63435             };
63436
63437             for (var layerID in photoOverlayLayers) {
63438               var layer = context.layers().layer(layerID);
63439               if (layer && layer.enabled()) {
63440                 photoOverlaysUsed.push(layerID);
63441                 imageryUsed.push(photoOverlayLayers[layerID]);
63442               }
63443             }
63444
63445             context.history().imageryUsed(imageryUsed);
63446             context.history().photoOverlaysUsed(photoOverlaysUsed);
63447           };
63448
63449
63450           background.sources = function (extent, zoom, includeCurrent) {
63451             if (!_imageryIndex) { return []; }   // called before init()?
63452
63453             var visible = {};
63454             (_imageryIndex.query.bbox(extent.rectangle(), true) || [])
63455               .forEach(function (d) { return visible[d.id] = true; });
63456
63457             var currSource = baseLayer.source();
63458
63459             return _imageryIndex.backgrounds.filter(function (source) {
63460               if (!source.polygon) { return true; }                          // always include imagery with worldwide coverage
63461               if (includeCurrent && currSource === source) { return true; }  // optionally include the current imagery
63462               if (zoom && zoom < 6) { return false; }                        // optionally exclude local imagery at low zooms
63463               return visible[source.id];                                 // include imagery visible in given extent
63464             });
63465           };
63466
63467
63468           background.dimensions = function (val) {
63469             if (!val) { return; }
63470             baseLayer.dimensions(val);
63471             _overlayLayers.forEach(function (layer) { return layer.dimensions(val); });
63472           };
63473
63474
63475           background.baseLayerSource = function(d) {
63476             if (!arguments.length) { return baseLayer.source(); }
63477
63478             // test source against OSM imagery blacklists..
63479             var osm = context.connection();
63480             if (!osm) { return background; }
63481
63482             var blacklists = osm.imageryBlacklists();
63483             var template = d.template();
63484             var fail = false;
63485             var tested = 0;
63486             var regex;
63487
63488             for (var i = 0; i < blacklists.length; i++) {
63489               try {
63490                 regex = new RegExp(blacklists[i]);
63491                 fail = regex.test(template);
63492                 tested++;
63493                 if (fail) { break; }
63494               } catch (e) {
63495                 /* noop */
63496               }
63497             }
63498
63499             // ensure at least one test was run.
63500             if (!tested) {
63501               regex = new RegExp('.*\.google(apis)?\..*/(vt|kh)[\?/].*([xyz]=.*){3}.*');
63502               fail = regex.test(template);
63503             }
63504
63505             baseLayer.source(!fail ? d : background.findSource('none'));
63506             dispatch$1.call('change');
63507             background.updateImagery();
63508             return background;
63509           };
63510
63511
63512           background.findSource = function (id) {
63513             if (!id || !_imageryIndex) { return null; }   // called before init()?
63514             return _imageryIndex.backgrounds.find(function (d) { return d.id && d.id === id; });
63515           };
63516
63517
63518           background.bing = function () {
63519             background.baseLayerSource(background.findSource('Bing'));
63520           };
63521
63522
63523           background.showsLayer = function (d) {
63524             var currSource = baseLayer.source();
63525             if (!d || !currSource) { return false; }
63526             return d.id === currSource.id || _overlayLayers.some(function (layer) { return d.id === layer.source().id; });
63527           };
63528
63529
63530           background.overlayLayerSources = function () {
63531             return _overlayLayers.map(function (layer) { return layer.source(); });
63532           };
63533
63534
63535           background.toggleOverlayLayer = function (d) {
63536             var layer;
63537             for (var i = 0; i < _overlayLayers.length; i++) {
63538               layer = _overlayLayers[i];
63539               if (layer.source() === d) {
63540                 _overlayLayers.splice(i, 1);
63541                 dispatch$1.call('change');
63542                 background.updateImagery();
63543                 return;
63544               }
63545             }
63546
63547             layer = rendererTileLayer(context)
63548               .source(d)
63549               .projection(context.projection)
63550               .dimensions(baseLayer.dimensions()
63551             );
63552
63553             _overlayLayers.push(layer);
63554             dispatch$1.call('change');
63555             background.updateImagery();
63556           };
63557
63558
63559           background.nudge = function (d, zoom) {
63560             var currSource = baseLayer.source();
63561             if (currSource) {
63562               currSource.nudge(d, zoom);
63563               dispatch$1.call('change');
63564               background.updateImagery();
63565             }
63566             return background;
63567           };
63568
63569
63570           background.offset = function(d) {
63571             var currSource = baseLayer.source();
63572             if (!arguments.length) {
63573               return (currSource && currSource.offset()) || [0, 0];
63574             }
63575             if (currSource) {
63576               currSource.offset(d);
63577               dispatch$1.call('change');
63578               background.updateImagery();
63579             }
63580             return background;
63581           };
63582
63583
63584           background.brightness = function(d) {
63585             if (!arguments.length) { return _brightness; }
63586             _brightness = d;
63587             if (context.mode()) { dispatch$1.call('change'); }
63588             return background;
63589           };
63590
63591
63592           background.contrast = function(d) {
63593             if (!arguments.length) { return _contrast; }
63594             _contrast = d;
63595             if (context.mode()) { dispatch$1.call('change'); }
63596             return background;
63597           };
63598
63599
63600           background.saturation = function(d) {
63601             if (!arguments.length) { return _saturation; }
63602             _saturation = d;
63603             if (context.mode()) { dispatch$1.call('change'); }
63604             return background;
63605           };
63606
63607
63608           background.sharpness = function(d) {
63609             if (!arguments.length) { return _sharpness; }
63610             _sharpness = d;
63611             if (context.mode()) { dispatch$1.call('change'); }
63612             return background;
63613           };
63614
63615           var _loadPromise;
63616
63617           background.ensureLoaded = function () {
63618
63619             if (_loadPromise) { return _loadPromise; }
63620
63621             function parseMapParams(qmap) {
63622               if (!qmap) { return false; }
63623               var params = qmap.split('/').map(Number);
63624               if (params.length < 3 || params.some(isNaN)) { return false; }
63625               return geoExtent([params[2], params[1]]);  // lon,lat
63626             }
63627
63628             var hash = utilStringQs(window.location.hash);
63629             var requested = hash.background || hash.layer;
63630             var extent = parseMapParams(hash.map);
63631
63632             return _loadPromise = ensureImageryIndex()
63633               .then(function (imageryIndex) {
63634                 var first = imageryIndex.backgrounds.length && imageryIndex.backgrounds[0];
63635
63636                 var best;
63637                 if (!requested && extent) {
63638                   best = background.sources(extent).find(function (s) { return s.best(); });
63639                 }
63640
63641                 // Decide which background layer to display
63642                 if (requested && requested.indexOf('custom:') === 0) {
63643                   var template = requested.replace(/^custom:/, '');
63644                   var custom = background.findSource('custom');
63645                   background.baseLayerSource(custom.template(template));
63646                   corePreferences('background-custom-template', template);
63647                 } else {
63648                   background.baseLayerSource(
63649                     background.findSource(requested) ||
63650                     best ||
63651                     background.findSource(corePreferences('background-last-used')) ||
63652                     background.findSource('Bing') ||
63653                     first ||
63654                     background.findSource('none')
63655                   );
63656                 }
63657
63658                 var locator = imageryIndex.backgrounds.find(function (d) { return d.overlay && d.default; });
63659                 if (locator) {
63660                   background.toggleOverlayLayer(locator);
63661                 }
63662
63663                 var overlays = (hash.overlays || '').split(',');
63664                 overlays.forEach(function (overlay) {
63665                   overlay = background.findSource(overlay);
63666                   if (overlay) {
63667                     background.toggleOverlayLayer(overlay);
63668                   }
63669                 });
63670
63671                 if (hash.gpx) {
63672                   var gpx = context.layers().layer('data');
63673                   if (gpx) {
63674                     gpx.url(hash.gpx, '.gpx');
63675                   }
63676                 }
63677
63678                 if (hash.offset) {
63679                   var offset = hash.offset
63680                     .replace(/;/g, ',')
63681                     .split(',')
63682                     .map(function (n) { return !isNaN(n) && n; });
63683
63684                   if (offset.length === 2) {
63685                     background.offset(geoMetersToOffset(offset));
63686                   }
63687                 }
63688               })
63689               .catch(function () { /* ignore */ });
63690           };
63691
63692
63693           return utilRebind(background, dispatch$1, 'on');
63694         }
63695
63696         function rendererFeatures(context) {
63697             var dispatch$1 = dispatch('change', 'redraw');
63698             var features = utilRebind({}, dispatch$1, 'on');
63699             var _deferred = new Set();
63700
63701             var traffic_roads = {
63702                 'motorway': true,
63703                 'motorway_link': true,
63704                 'trunk': true,
63705                 'trunk_link': true,
63706                 'primary': true,
63707                 'primary_link': true,
63708                 'secondary': true,
63709                 'secondary_link': true,
63710                 'tertiary': true,
63711                 'tertiary_link': true,
63712                 'residential': true,
63713                 'unclassified': true,
63714                 'living_street': true
63715             };
63716
63717             var service_roads = {
63718                 'service': true,
63719                 'road': true,
63720                 'track': true
63721             };
63722
63723             var paths = {
63724                 'path': true,
63725                 'footway': true,
63726                 'cycleway': true,
63727                 'bridleway': true,
63728                 'steps': true,
63729                 'pedestrian': true
63730             };
63731
63732             var past_futures = {
63733                 'proposed': true,
63734                 'construction': true,
63735                 'abandoned': true,
63736                 'dismantled': true,
63737                 'disused': true,
63738                 'razed': true,
63739                 'demolished': true,
63740                 'obliterated': true
63741             };
63742
63743             var _cullFactor = 1;
63744             var _cache = {};
63745             var _rules = {};
63746             var _stats = {};
63747             var _keys = [];
63748             var _hidden = [];
63749             var _forceVisible = {};
63750
63751
63752             function update() {
63753                 if (!window.mocha) {
63754                     var hash = utilStringQs(window.location.hash);
63755                     var disabled = features.disabled();
63756                     if (disabled.length) {
63757                         hash.disable_features = disabled.join(',');
63758                     } else {
63759                         delete hash.disable_features;
63760                     }
63761                     window.location.replace('#' + utilQsString(hash, true));
63762                     corePreferences('disabled-features', disabled.join(','));
63763                 }
63764                 _hidden = features.hidden();
63765                 dispatch$1.call('change');
63766                 dispatch$1.call('redraw');
63767             }
63768
63769
63770             function defineRule(k, filter, max) {
63771                 var isEnabled = true;
63772
63773                 _keys.push(k);
63774                 _rules[k] = {
63775                     filter: filter,
63776                     enabled: isEnabled,   // whether the user wants it enabled..
63777                     count: 0,
63778                     currentMax: (max || Infinity),
63779                     defaultMax: (max || Infinity),
63780                     enable: function() { this.enabled = true; this.currentMax = this.defaultMax; },
63781                     disable: function() { this.enabled = false; this.currentMax = 0; },
63782                     hidden: function() {
63783                         return (this.count === 0 && !this.enabled) ||
63784                             this.count > this.currentMax * _cullFactor;
63785                     },
63786                     autoHidden: function() { return this.hidden() && this.currentMax > 0; }
63787                 };
63788             }
63789
63790
63791             defineRule('points', function isPoint(tags, geometry) {
63792                 return geometry === 'point';
63793             }, 200);
63794
63795             defineRule('traffic_roads', function isTrafficRoad(tags) {
63796                 return traffic_roads[tags.highway];
63797             });
63798
63799             defineRule('service_roads', function isServiceRoad(tags) {
63800                 return service_roads[tags.highway];
63801             });
63802
63803             defineRule('paths', function isPath(tags) {
63804                 return paths[tags.highway];
63805             });
63806
63807             defineRule('buildings', function isBuilding(tags) {
63808                 return (
63809                     (!!tags.building && tags.building !== 'no') ||
63810                     tags.parking === 'multi-storey' ||
63811                     tags.parking === 'sheds' ||
63812                     tags.parking === 'carports' ||
63813                     tags.parking === 'garage_boxes'
63814                 );
63815             }, 250);
63816
63817             defineRule('building_parts', function isBuildingPart(tags) {
63818                 return tags['building:part'];
63819             });
63820
63821             defineRule('indoor', function isIndoor(tags) {
63822                 return tags.indoor;
63823             });
63824
63825             defineRule('landuse', function isLanduse(tags, geometry) {
63826                 return geometry === 'area' &&
63827                     !_rules.buildings.filter(tags) &&
63828                     !_rules.building_parts.filter(tags) &&
63829                     !_rules.indoor.filter(tags) &&
63830                     !_rules.water.filter(tags) &&
63831                     !_rules.pistes.filter(tags);
63832             });
63833
63834             defineRule('boundaries', function isBoundary(tags) {
63835                 return (
63836                     !!tags.boundary
63837                 ) && !(
63838                     traffic_roads[tags.highway] ||
63839                     service_roads[tags.highway] ||
63840                     paths[tags.highway] ||
63841                     tags.waterway ||
63842                     tags.railway ||
63843                     tags.landuse ||
63844                     tags.natural ||
63845                     tags.building ||
63846                     tags.power
63847                 );
63848             });
63849
63850             defineRule('water', function isWater(tags) {
63851                 return (
63852                     !!tags.waterway ||
63853                     tags.natural === 'water' ||
63854                     tags.natural === 'coastline' ||
63855                     tags.natural === 'bay' ||
63856                     tags.landuse === 'pond' ||
63857                     tags.landuse === 'basin' ||
63858                     tags.landuse === 'reservoir' ||
63859                     tags.landuse === 'salt_pond'
63860                 );
63861             });
63862
63863             defineRule('rail', function isRail(tags) {
63864                 return (
63865                     !!tags.railway ||
63866                     tags.landuse === 'railway'
63867                 ) && !(
63868                     traffic_roads[tags.highway] ||
63869                     service_roads[tags.highway] ||
63870                     paths[tags.highway]
63871                 );
63872             });
63873
63874             defineRule('pistes', function isPiste(tags) {
63875                 return tags['piste:type'];
63876             });
63877
63878             defineRule('aerialways', function isPiste(tags) {
63879                 return tags.aerialway &&
63880                     tags.aerialway !== 'yes' &&
63881                     tags.aerialway !== 'station';
63882             });
63883
63884             defineRule('power', function isPower(tags) {
63885                 return !!tags.power;
63886             });
63887
63888             // contains a past/future tag, but not in active use as a road/path/cycleway/etc..
63889             defineRule('past_future', function isPastFuture(tags) {
63890                 if (
63891                     traffic_roads[tags.highway] ||
63892                     service_roads[tags.highway] ||
63893                     paths[tags.highway]
63894                 ) { return false; }
63895
63896                 var strings = Object.keys(tags);
63897
63898                 for (var i = 0; i < strings.length; i++) {
63899                     var s = strings[i];
63900                     if (past_futures[s] || past_futures[tags[s]]) { return true; }
63901                 }
63902                 return false;
63903             });
63904
63905             // Lines or areas that don't match another feature filter.
63906             // IMPORTANT: The 'others' feature must be the last one defined,
63907             //   so that code in getMatches can skip this test if `hasMatch = true`
63908             defineRule('others', function isOther(tags, geometry) {
63909                 return (geometry === 'line' || geometry === 'area');
63910             });
63911
63912
63913
63914             features.features = function() {
63915                 return _rules;
63916             };
63917
63918
63919             features.keys = function() {
63920                 return _keys;
63921             };
63922
63923
63924             features.enabled = function(k) {
63925                 if (!arguments.length) {
63926                     return _keys.filter(function(k) { return _rules[k].enabled; });
63927                 }
63928                 return _rules[k] && _rules[k].enabled;
63929             };
63930
63931
63932             features.disabled = function(k) {
63933                 if (!arguments.length) {
63934                     return _keys.filter(function(k) { return !_rules[k].enabled; });
63935                 }
63936                 return _rules[k] && !_rules[k].enabled;
63937             };
63938
63939
63940             features.hidden = function(k) {
63941                 if (!arguments.length) {
63942                     return _keys.filter(function(k) { return _rules[k].hidden(); });
63943                 }
63944                 return _rules[k] && _rules[k].hidden();
63945             };
63946
63947
63948             features.autoHidden = function(k) {
63949                 if (!arguments.length) {
63950                     return _keys.filter(function(k) { return _rules[k].autoHidden(); });
63951                 }
63952                 return _rules[k] && _rules[k].autoHidden();
63953             };
63954
63955
63956             features.enable = function(k) {
63957                 if (_rules[k] && !_rules[k].enabled) {
63958                     _rules[k].enable();
63959                     update();
63960                 }
63961             };
63962
63963             features.enableAll = function() {
63964                 var didEnable = false;
63965                 for (var k in _rules) {
63966                     if (!_rules[k].enabled) {
63967                         didEnable = true;
63968                         _rules[k].enable();
63969                     }
63970                 }
63971                 if (didEnable) { update(); }
63972             };
63973
63974
63975             features.disable = function(k) {
63976                 if (_rules[k] && _rules[k].enabled) {
63977                     _rules[k].disable();
63978                     update();
63979                 }
63980             };
63981
63982             features.disableAll = function() {
63983                 var didDisable = false;
63984                 for (var k in _rules) {
63985                     if (_rules[k].enabled) {
63986                         didDisable = true;
63987                         _rules[k].disable();
63988                     }
63989                 }
63990                 if (didDisable) { update(); }
63991             };
63992
63993
63994             features.toggle = function(k) {
63995                 if (_rules[k]) {
63996                     (function(f) { return f.enabled ? f.disable() : f.enable(); }(_rules[k]));
63997                     update();
63998                 }
63999             };
64000
64001
64002             features.resetStats = function() {
64003                 for (var i = 0; i < _keys.length; i++) {
64004                     _rules[_keys[i]].count = 0;
64005                 }
64006                 dispatch$1.call('change');
64007             };
64008
64009
64010             features.gatherStats = function(d, resolver, dimensions) {
64011                 var needsRedraw = false;
64012                 var types = utilArrayGroupBy(d, 'type');
64013                 var entities = [].concat(types.relation || [], types.way || [], types.node || []);
64014                 var currHidden, geometry, matches, i, j;
64015
64016                 for (i = 0; i < _keys.length; i++) {
64017                     _rules[_keys[i]].count = 0;
64018                 }
64019
64020                 // adjust the threshold for point/building culling based on viewport size..
64021                 // a _cullFactor of 1 corresponds to a 1000x1000px viewport..
64022                 _cullFactor = dimensions[0] * dimensions[1] / 1000000;
64023
64024                 for (i = 0; i < entities.length; i++) {
64025                     geometry = entities[i].geometry(resolver);
64026                     matches = Object.keys(features.getMatches(entities[i], resolver, geometry));
64027                     for (j = 0; j < matches.length; j++) {
64028                         _rules[matches[j]].count++;
64029                     }
64030                 }
64031
64032                 currHidden = features.hidden();
64033                 if (currHidden !== _hidden) {
64034                     _hidden = currHidden;
64035                     needsRedraw = true;
64036                     dispatch$1.call('change');
64037                 }
64038
64039                 return needsRedraw;
64040             };
64041
64042
64043             features.stats = function() {
64044                 for (var i = 0; i < _keys.length; i++) {
64045                     _stats[_keys[i]] = _rules[_keys[i]].count;
64046                 }
64047
64048                 return _stats;
64049             };
64050
64051
64052             features.clear = function(d) {
64053                 for (var i = 0; i < d.length; i++) {
64054                     features.clearEntity(d[i]);
64055                 }
64056             };
64057
64058
64059             features.clearEntity = function(entity) {
64060                 delete _cache[osmEntity.key(entity)];
64061             };
64062
64063
64064             features.reset = function() {
64065                 Array.from(_deferred).forEach(function(handle) {
64066                     window.cancelIdleCallback(handle);
64067                     _deferred.delete(handle);
64068                 });
64069
64070                 _cache = {};
64071             };
64072
64073             // only certain relations are worth checking
64074             function relationShouldBeChecked(relation) {
64075                 // multipolygon features have `area` geometry and aren't checked here
64076                 return relation.tags.type === 'boundary';
64077             }
64078
64079             features.getMatches = function(entity, resolver, geometry) {
64080                 if (geometry === 'vertex' ||
64081                     (geometry === 'relation' && !relationShouldBeChecked(entity))) { return {}; }
64082
64083                 var ent = osmEntity.key(entity);
64084                 if (!_cache[ent]) {
64085                     _cache[ent] = {};
64086                 }
64087
64088                 if (!_cache[ent].matches) {
64089                     var matches = {};
64090                     var hasMatch = false;
64091
64092                     for (var i = 0; i < _keys.length; i++) {
64093                         if (_keys[i] === 'others') {
64094                             if (hasMatch) { continue; }
64095
64096                             // If an entity...
64097                             //   1. is a way that hasn't matched other 'interesting' feature rules,
64098                             if (entity.type === 'way') {
64099                                 var parents = features.getParents(entity, resolver, geometry);
64100
64101                                 //   2a. belongs only to a single multipolygon relation
64102                                 if ((parents.length === 1 && parents[0].isMultipolygon()) ||
64103                                     // 2b. or belongs only to boundary relations
64104                                     (parents.length > 0 && parents.every(function(parent) { return parent.tags.type === 'boundary'; }))) {
64105
64106                                     // ...then match whatever feature rules the parent relation has matched.
64107                                     // see #2548, #2887
64108                                     //
64109                                     // IMPORTANT:
64110                                     // For this to work, getMatches must be called on relations before ways.
64111                                     //
64112                                     var pkey = osmEntity.key(parents[0]);
64113                                     if (_cache[pkey] && _cache[pkey].matches) {
64114                                         matches = Object.assign({}, _cache[pkey].matches);  // shallow copy
64115                                         continue;
64116                                     }
64117                                 }
64118                             }
64119                         }
64120
64121                         if (_rules[_keys[i]].filter(entity.tags, geometry)) {
64122                             matches[_keys[i]] = hasMatch = true;
64123                         }
64124                     }
64125                     _cache[ent].matches = matches;
64126                 }
64127
64128                 return _cache[ent].matches;
64129             };
64130
64131
64132             features.getParents = function(entity, resolver, geometry) {
64133                 if (geometry === 'point') { return []; }
64134
64135                 var ent = osmEntity.key(entity);
64136                 if (!_cache[ent]) {
64137                     _cache[ent] = {};
64138                 }
64139
64140                 if (!_cache[ent].parents) {
64141                     var parents = [];
64142                     if (geometry === 'vertex') {
64143                         parents = resolver.parentWays(entity);
64144                     } else {   // 'line', 'area', 'relation'
64145                         parents = resolver.parentRelations(entity);
64146                     }
64147                     _cache[ent].parents = parents;
64148                 }
64149                 return _cache[ent].parents;
64150             };
64151
64152
64153             features.isHiddenPreset = function(preset, geometry) {
64154                 if (!_hidden.length) { return false; }
64155                 if (!preset.tags) { return false; }
64156
64157                 var test = preset.setTags({}, geometry);
64158                 for (var key in _rules) {
64159                     if (_rules[key].filter(test, geometry)) {
64160                         if (_hidden.indexOf(key) !== -1) {
64161                             return key;
64162                         }
64163                         return false;
64164                     }
64165                 }
64166                 return false;
64167             };
64168
64169
64170             features.isHiddenFeature = function(entity, resolver, geometry) {
64171                 if (!_hidden.length) { return false; }
64172                 if (!entity.version) { return false; }
64173                 if (_forceVisible[entity.id]) { return false; }
64174
64175                 var matches = Object.keys(features.getMatches(entity, resolver, geometry));
64176                 return matches.length && matches.every(function(k) { return features.hidden(k); });
64177             };
64178
64179
64180             features.isHiddenChild = function(entity, resolver, geometry) {
64181                 if (!_hidden.length) { return false; }
64182                 if (!entity.version || geometry === 'point') { return false; }
64183                 if (_forceVisible[entity.id]) { return false; }
64184
64185                 var parents = features.getParents(entity, resolver, geometry);
64186                 if (!parents.length) { return false; }
64187
64188                 for (var i = 0; i < parents.length; i++) {
64189                     if (!features.isHidden(parents[i], resolver, parents[i].geometry(resolver))) {
64190                         return false;
64191                     }
64192                 }
64193                 return true;
64194             };
64195
64196
64197             features.hasHiddenConnections = function(entity, resolver) {
64198                 if (!_hidden.length) { return false; }
64199
64200                 var childNodes, connections;
64201                 if (entity.type === 'midpoint') {
64202                     childNodes = [resolver.entity(entity.edge[0]), resolver.entity(entity.edge[1])];
64203                     connections = [];
64204                 } else {
64205                     childNodes = entity.nodes ? resolver.childNodes(entity) : [];
64206                     connections = features.getParents(entity, resolver, entity.geometry(resolver));
64207                 }
64208
64209                 // gather ways connected to child nodes..
64210                 connections = childNodes.reduce(function(result, e) {
64211                     return resolver.isShared(e) ? utilArrayUnion(result, resolver.parentWays(e)) : result;
64212                 }, connections);
64213
64214                 return connections.some(function(e) {
64215                     return features.isHidden(e, resolver, e.geometry(resolver));
64216                 });
64217             };
64218
64219
64220             features.isHidden = function(entity, resolver, geometry) {
64221                 if (!_hidden.length) { return false; }
64222                 if (!entity.version) { return false; }
64223
64224                 var fn = (geometry === 'vertex' ? features.isHiddenChild : features.isHiddenFeature);
64225                 return fn(entity, resolver, geometry);
64226             };
64227
64228
64229             features.filter = function(d, resolver) {
64230                 if (!_hidden.length) { return d; }
64231
64232                 var result = [];
64233                 for (var i = 0; i < d.length; i++) {
64234                     var entity = d[i];
64235                     if (!features.isHidden(entity, resolver, entity.geometry(resolver))) {
64236                         result.push(entity);
64237                     }
64238                 }
64239                 return result;
64240             };
64241
64242
64243             features.forceVisible = function(entityIDs) {
64244                 if (!arguments.length) { return Object.keys(_forceVisible); }
64245
64246                 _forceVisible = {};
64247                 for (var i = 0; i < entityIDs.length; i++) {
64248                     _forceVisible[entityIDs[i]] = true;
64249                     var entity = context.hasEntity(entityIDs[i]);
64250                     if (entity && entity.type === 'relation') {
64251                         // also show relation members (one level deep)
64252                         for (var j in entity.members) {
64253                             _forceVisible[entity.members[j].id] = true;
64254                         }
64255                     }
64256                 }
64257                 return features;
64258             };
64259
64260
64261             features.init = function() {
64262                 var storage = corePreferences('disabled-features');
64263                 if (storage) {
64264                     var storageDisabled = storage.replace(/;/g, ',').split(',');
64265                     storageDisabled.forEach(features.disable);
64266                 }
64267
64268                 var hash = utilStringQs(window.location.hash);
64269                 if (hash.disable_features) {
64270                     var hashDisabled = hash.disable_features.replace(/;/g, ',').split(',');
64271                     hashDisabled.forEach(features.disable);
64272                 }
64273             };
64274
64275
64276             // warm up the feature matching cache upon merging fetched data
64277             context.history().on('merge.features', function(newEntities) {
64278                 if (!newEntities) { return; }
64279                 var handle = window.requestIdleCallback(function() {
64280                     var graph = context.graph();
64281                     var types = utilArrayGroupBy(newEntities, 'type');
64282                     // ensure that getMatches is called on relations before ways
64283                     var entities = [].concat(types.relation || [], types.way || [], types.node || []);
64284                     for (var i = 0; i < entities.length; i++) {
64285                         var geometry = entities[i].geometry(graph);
64286                         features.getMatches(entities[i], graph, geometry);
64287                     }
64288                 });
64289                 _deferred.add(handle);
64290             });
64291
64292
64293             return features;
64294         }
64295
64296         // Touch targets control which other vertices we can drag a vertex onto.
64297         //
64298         // - the activeID - nope
64299         // - 1 away (adjacent) to the activeID - yes (vertices will be merged)
64300         // - 2 away from the activeID - nope (would create a self intersecting segment)
64301         // - all others on a linear way - yes
64302         // - all others on a closed way - nope (would create a self intersecting polygon)
64303         //
64304         // returns
64305         // 0 = active vertex - no touch/connect
64306         // 1 = passive vertex - yes touch/connect
64307         // 2 = adjacent vertex - yes but pay attention segmenting a line here
64308         //
64309         function svgPassiveVertex(node, graph, activeID) {
64310             if (!activeID) { return 1; }
64311             if (activeID === node.id) { return 0; }
64312
64313             var parents = graph.parentWays(node);
64314
64315             var i, j, nodes, isClosed, ix1, ix2, ix3, ix4, max;
64316
64317             for (i = 0; i < parents.length; i++) {
64318                 nodes = parents[i].nodes;
64319                 isClosed = parents[i].isClosed();
64320                 for (j = 0; j < nodes.length; j++) {   // find this vertex, look nearby
64321                     if (nodes[j] === node.id) {
64322                         ix1 = j - 2;
64323                         ix2 = j - 1;
64324                         ix3 = j + 1;
64325                         ix4 = j + 2;
64326
64327                         if (isClosed) {  // wraparound if needed
64328                             max = nodes.length - 1;
64329                             if (ix1 < 0)   { ix1 = max + ix1; }
64330                             if (ix2 < 0)   { ix2 = max + ix2; }
64331                             if (ix3 > max) { ix3 = ix3 - max; }
64332                             if (ix4 > max) { ix4 = ix4 - max; }
64333                         }
64334
64335                         if (nodes[ix1] === activeID) { return 0; }        // no - prevent self intersect
64336                         else if (nodes[ix2] === activeID) { return 2; }   // ok - adjacent
64337                         else if (nodes[ix3] === activeID) { return 2; }   // ok - adjacent
64338                         else if (nodes[ix4] === activeID) { return 0; }   // no - prevent self intersect
64339                         else if (isClosed && nodes.indexOf(activeID) !== -1) { return 0; }  // no - prevent self intersect
64340                     }
64341                 }
64342             }
64343
64344             return 1;   // ok
64345         }
64346
64347
64348         function svgMarkerSegments(projection, graph, dt,
64349                                           shouldReverse,
64350                                           bothDirections) {
64351             return function(entity) {
64352                 var i = 0;
64353                 var offset = dt;
64354                 var segments = [];
64355                 var clip = d3_geoIdentity().clipExtent(projection.clipExtent()).stream;
64356                 var coordinates = graph.childNodes(entity).map(function(n) { return n.loc; });
64357                 var a, b;
64358
64359                 if (shouldReverse(entity)) {
64360                     coordinates.reverse();
64361                 }
64362
64363                 d3_geoStream({
64364                     type: 'LineString',
64365                     coordinates: coordinates
64366                 }, projection.stream(clip({
64367                     lineStart: function() {},
64368                     lineEnd: function() { a = null; },
64369                     point: function(x, y) {
64370                         b = [x, y];
64371
64372                         if (a) {
64373                             var span = geoVecLength(a, b) - offset;
64374
64375                             if (span >= 0) {
64376                                 var heading = geoVecAngle(a, b);
64377                                 var dx = dt * Math.cos(heading);
64378                                 var dy = dt * Math.sin(heading);
64379                                 var p = [
64380                                     a[0] + offset * Math.cos(heading),
64381                                     a[1] + offset * Math.sin(heading)
64382                                 ];
64383
64384                                 // gather coordinates
64385                                 var coord = [a, p];
64386                                 for (span -= dt; span >= 0; span -= dt) {
64387                                     p = geoVecAdd(p, [dx, dy]);
64388                                     coord.push(p);
64389                                 }
64390                                 coord.push(b);
64391
64392                                 // generate svg paths
64393                                 var segment = '';
64394                                 var j;
64395
64396                                 for (j = 0; j < coord.length; j++) {
64397                                     segment += (j === 0 ? 'M' : 'L') + coord[j][0] + ',' + coord[j][1];
64398                                 }
64399                                 segments.push({ id: entity.id, index: i++, d: segment });
64400
64401                                 if (bothDirections(entity)) {
64402                                     segment = '';
64403                                     for (j = coord.length - 1; j >= 0; j--) {
64404                                         segment += (j === coord.length - 1 ? 'M' : 'L') + coord[j][0] + ',' + coord[j][1];
64405                                     }
64406                                     segments.push({ id: entity.id, index: i++, d: segment });
64407                                 }
64408                             }
64409
64410                             offset = -span;
64411                         }
64412
64413                         a = b;
64414                     }
64415                 })));
64416
64417                 return segments;
64418             };
64419         }
64420
64421
64422         function svgPath(projection, graph, isArea) {
64423
64424             // Explanation of magic numbers:
64425             // "padding" here allows space for strokes to extend beyond the viewport,
64426             // so that the stroke isn't drawn along the edge of the viewport when
64427             // the shape is clipped.
64428             //
64429             // When drawing lines, pad viewport by 5px.
64430             // When drawing areas, pad viewport by 65px in each direction to allow
64431             // for 60px area fill stroke (see ".fill-partial path.fill" css rule)
64432
64433             var cache = {};
64434             var padding = isArea ? 65 : 5;
64435             var viewport = projection.clipExtent();
64436             var paddedExtent = [
64437                 [viewport[0][0] - padding, viewport[0][1] - padding],
64438                 [viewport[1][0] + padding, viewport[1][1] + padding]
64439             ];
64440             var clip = d3_geoIdentity().clipExtent(paddedExtent).stream;
64441             var project = projection.stream;
64442             var path = d3_geoPath()
64443                 .projection({stream: function(output) { return project(clip(output)); }});
64444
64445             var svgpath = function(entity) {
64446                 if (entity.id in cache) {
64447                     return cache[entity.id];
64448                 } else {
64449                     return cache[entity.id] = path(entity.asGeoJSON(graph));
64450                 }
64451             };
64452
64453             svgpath.geojson = function(d) {
64454                 if (d.__featurehash__ !== undefined) {
64455                     if (d.__featurehash__ in cache) {
64456                         return cache[d.__featurehash__];
64457                     } else {
64458                         return cache[d.__featurehash__] = path(d);
64459                     }
64460                 } else {
64461                     return path(d);
64462                 }
64463             };
64464
64465             return svgpath;
64466         }
64467
64468
64469         function svgPointTransform(projection) {
64470             var svgpoint = function(entity) {
64471                 // http://jsperf.com/short-array-join
64472                 var pt = projection(entity.loc);
64473                 return 'translate(' + pt[0] + ',' + pt[1] + ')';
64474             };
64475
64476             svgpoint.geojson = function(d) {
64477                 return svgpoint(d.properties.entity);
64478             };
64479
64480             return svgpoint;
64481         }
64482
64483
64484         function svgRelationMemberTags(graph) {
64485             return function(entity) {
64486                 var tags = entity.tags;
64487                 var shouldCopyMultipolygonTags = !entity.hasInterestingTags();
64488                 graph.parentRelations(entity).forEach(function(relation) {
64489                     var type = relation.tags.type;
64490                     if ((type === 'multipolygon' && shouldCopyMultipolygonTags) || type === 'boundary') {
64491                         tags = Object.assign({}, relation.tags, tags);
64492                     }
64493                 });
64494                 return tags;
64495             };
64496         }
64497
64498
64499         function svgSegmentWay(way, graph, activeID) {
64500             // When there is no activeID, we can memoize this expensive computation
64501             if (activeID === undefined) {
64502                 return graph.transient(way, 'waySegments', getWaySegments);
64503             } else {
64504                 return getWaySegments();
64505             }
64506
64507             function getWaySegments() {
64508                 var isActiveWay = (way.nodes.indexOf(activeID) !== -1);
64509                 var features = { passive: [], active: [] };
64510                 var start = {};
64511                 var end = {};
64512                 var node, type;
64513
64514                 for (var i = 0; i < way.nodes.length; i++) {
64515                     node = graph.entity(way.nodes[i]);
64516                     type = svgPassiveVertex(node, graph, activeID);
64517                     end = { node: node, type: type };
64518
64519                     if (start.type !== undefined) {
64520                         if (start.node.id === activeID || end.node.id === activeID) ; else if (isActiveWay && (start.type === 2 || end.type === 2)) {   // one adjacent vertex
64521                             pushActive(start, end, i);
64522                         } else if (start.type === 0 && end.type === 0) {   // both active vertices
64523                             pushActive(start, end, i);
64524                         } else {
64525                             pushPassive(start, end, i);
64526                         }
64527                     }
64528
64529                     start = end;
64530                 }
64531
64532                 return features;
64533
64534                 function pushActive(start, end, index) {
64535                     features.active.push({
64536                         type: 'Feature',
64537                         id: way.id + '-' + index + '-nope',
64538                         properties: {
64539                             nope: true,
64540                             target: true,
64541                             entity: way,
64542                             nodes: [start.node, end.node],
64543                             index: index
64544                         },
64545                         geometry: {
64546                             type: 'LineString',
64547                             coordinates: [start.node.loc, end.node.loc]
64548                         }
64549                     });
64550                 }
64551
64552                 function pushPassive(start, end, index) {
64553                     features.passive.push({
64554                         type: 'Feature',
64555                         id: way.id + '-' + index,
64556                         properties: {
64557                             target: true,
64558                             entity: way,
64559                             nodes: [start.node, end.node],
64560                             index: index
64561                         },
64562                         geometry: {
64563                             type: 'LineString',
64564                             coordinates: [start.node.loc, end.node.loc]
64565                         }
64566                     });
64567                 }
64568             }
64569         }
64570
64571         function svgTagClasses() {
64572             var primaries = [
64573                 'building', 'highway', 'railway', 'waterway', 'aeroway', 'aerialway',
64574                 'piste:type', 'boundary', 'power', 'amenity', 'natural', 'landuse',
64575                 'leisure', 'military', 'place', 'man_made', 'route', 'attraction',
64576                 'building:part', 'indoor'
64577             ];
64578             var statuses = [
64579                 // nonexistent, might be built
64580                 'proposed', 'planned',
64581                 // under maintentance or between groundbreaking and opening
64582                 'construction',
64583                 // existent but not functional
64584                 'disused',
64585                 // dilapidated to nonexistent
64586                 'abandoned',
64587                 // nonexistent, still may appear in imagery
64588                 'dismantled', 'razed', 'demolished', 'obliterated',
64589                 // existent occasionally, e.g. stormwater drainage basin
64590                 'intermittent'
64591             ];
64592             var secondaries = [
64593                 'oneway', 'bridge', 'tunnel', 'embankment', 'cutting', 'barrier',
64594                 'surface', 'tracktype', 'footway', 'crossing', 'service', 'sport',
64595                 'public_transport', 'location', 'parking', 'golf', 'type', 'leisure',
64596                 'man_made', 'indoor'
64597             ];
64598             var _tags = function(entity) { return entity.tags; };
64599
64600
64601             var tagClasses = function(selection) {
64602                 selection.each(function tagClassesEach(entity) {
64603                     var value = this.className;
64604
64605                     if (value.baseVal !== undefined) {
64606                         value = value.baseVal;
64607                     }
64608
64609                     var t = _tags(entity);
64610
64611                     var computed = tagClasses.getClassesString(t, value);
64612
64613                     if (computed !== value) {
64614                         select(this).attr('class', computed);
64615                     }
64616                 });
64617             };
64618
64619
64620             tagClasses.getClassesString = function(t, value) {
64621                 var primary, status;
64622                 var i, j, k, v;
64623
64624                 // in some situations we want to render perimeter strokes a certain way
64625                 var overrideGeometry;
64626                 if (/\bstroke\b/.test(value)) {
64627                     if (!!t.barrier && t.barrier !== 'no') {
64628                         overrideGeometry = 'line';
64629                     }
64630                 }
64631
64632                 // preserve base classes (nothing with `tag-`)
64633                 var classes = value.trim().split(/\s+/)
64634                     .filter(function(klass) {
64635                         return klass.length && !/^tag-/.test(klass);
64636                     })
64637                     .map(function(klass) {  // special overrides for some perimeter strokes
64638                         return (klass === 'line' || klass === 'area') ? (overrideGeometry || klass) : klass;
64639                     });
64640
64641                 // pick at most one primary classification tag..
64642                 for (i = 0; i < primaries.length; i++) {
64643                     k = primaries[i];
64644                     v = t[k];
64645                     if (!v || v === 'no') { continue; }
64646
64647                     if (k === 'piste:type') {  // avoid a ':' in the class name
64648                         k = 'piste';
64649                     } else if (k === 'building:part') {  // avoid a ':' in the class name
64650                         k = 'building_part';
64651                     }
64652
64653                     primary = k;
64654                     if (statuses.indexOf(v) !== -1) {   // e.g. `railway=abandoned`
64655                         status = v;
64656                         classes.push('tag-' + k);
64657                     } else {
64658                         classes.push('tag-' + k);
64659                         classes.push('tag-' + k + '-' + v);
64660                     }
64661
64662                     break;
64663                 }
64664
64665                 if (!primary) {
64666                     for (i = 0; i < statuses.length; i++) {
64667                         for (j = 0; j < primaries.length; j++) {
64668                             k = statuses[i] + ':' + primaries[j];  // e.g. `demolished:building=yes`
64669                             v = t[k];
64670                             if (!v || v === 'no') { continue; }
64671
64672                             status = statuses[i];
64673                             break;
64674                         }
64675                     }
64676                 }
64677
64678                 // add at most one status tag, only if relates to primary tag..
64679                 if (!status) {
64680                     for (i = 0; i < statuses.length; i++) {
64681                         k = statuses[i];
64682                         v = t[k];
64683                         if (!v || v === 'no') { continue; }
64684
64685                         if (v === 'yes') {   // e.g. `railway=rail + abandoned=yes`
64686                             status = k;
64687                         }
64688                         else if (primary && primary === v) {  // e.g. `railway=rail + abandoned=railway`
64689                             status = k;
64690                         } else if (!primary && primaries.indexOf(v) !== -1) {  // e.g. `abandoned=railway`
64691                             status = k;
64692                             primary = v;
64693                             classes.push('tag-' + v);
64694                         }  // else ignore e.g.  `highway=path + abandoned=railway`
64695
64696                         if (status) { break; }
64697                     }
64698                 }
64699
64700                 if (status) {
64701                     classes.push('tag-status');
64702                     classes.push('tag-status-' + status);
64703                 }
64704
64705                 // add any secondary tags
64706                 for (i = 0; i < secondaries.length; i++) {
64707                     k = secondaries[i];
64708                     v = t[k];
64709                     if (!v || v === 'no' || k === primary) { continue; }
64710                     classes.push('tag-' + k);
64711                     classes.push('tag-' + k + '-' + v);
64712                 }
64713
64714                 // For highways, look for surface tagging..
64715                 if ((primary === 'highway' && !osmPathHighwayTagValues[t.highway]) || primary === 'aeroway') {
64716                     var surface = t.highway === 'track' ? 'unpaved' : 'paved';
64717                     for (k in t) {
64718                         v = t[k];
64719                         if (k in osmPavedTags) {
64720                             surface = osmPavedTags[k][v] ? 'paved' : 'unpaved';
64721                         }
64722                         if (k in osmSemipavedTags && !!osmSemipavedTags[k][v]) {
64723                             surface = 'semipaved';
64724                         }
64725                     }
64726                     classes.push('tag-' + surface);
64727                 }
64728
64729                 // If this is a wikidata-tagged item, add a class for that..
64730                 if (t.wikidata || t['brand:wikidata']) {
64731                     classes.push('tag-wikidata');
64732                 }
64733
64734                 return classes.join(' ').trim();
64735             };
64736
64737
64738             tagClasses.tags = function(val) {
64739                 if (!arguments.length) { return _tags; }
64740                 _tags = val;
64741                 return tagClasses;
64742             };
64743
64744             return tagClasses;
64745         }
64746
64747         // Patterns only work in Firefox when set directly on element.
64748         // (This is not a bug: https://bugzilla.mozilla.org/show_bug.cgi?id=750632)
64749         var patterns = {
64750             // tag - pattern name
64751             // -or-
64752             // tag - value - pattern name
64753             // -or-
64754             // tag - value - rules (optional tag-values, pattern name)
64755             // (matches earlier rules first, so fallback should be last entry)
64756             amenity: {
64757                 grave_yard: 'cemetery',
64758                 fountain: 'water_standing'
64759             },
64760             landuse: {
64761                 cemetery: [
64762                     { religion: 'christian', pattern: 'cemetery_christian' },
64763                     { religion: 'buddhist', pattern: 'cemetery_buddhist' },
64764                     { religion: 'muslim', pattern: 'cemetery_muslim' },
64765                     { religion: 'jewish', pattern: 'cemetery_jewish' },
64766                     { pattern: 'cemetery' }
64767                 ],
64768                 construction: 'construction',
64769                 farmland: 'farmland',
64770                 farmyard: 'farmyard',
64771                 forest: [
64772                     { leaf_type: 'broadleaved', pattern: 'forest_broadleaved' },
64773                     { leaf_type: 'needleleaved', pattern: 'forest_needleleaved' },
64774                     { leaf_type: 'leafless', pattern: 'forest_leafless' },
64775                     { pattern: 'forest' } // same as 'leaf_type:mixed'
64776                 ],
64777                 grave_yard: 'cemetery',
64778                 grass: [
64779                     { golf: 'green', pattern: 'golf_green' },
64780                     { pattern: 'grass' } ],
64781                 landfill: 'landfill',
64782                 meadow: 'meadow',
64783                 military: 'construction',
64784                 orchard: 'orchard',
64785                 quarry: 'quarry',
64786                 vineyard: 'vineyard'
64787             },
64788             natural: {
64789                 beach: 'beach',
64790                 grassland: 'grass',
64791                 sand: 'beach',
64792                 scrub: 'scrub',
64793                 water: [
64794                     { water: 'pond', pattern: 'pond' },
64795                     { water: 'reservoir', pattern: 'water_standing' },
64796                     { pattern: 'waves' }
64797                 ],
64798                 wetland: [
64799                     { wetland: 'marsh', pattern: 'wetland_marsh' },
64800                     { wetland: 'swamp', pattern: 'wetland_swamp' },
64801                     { wetland: 'bog', pattern: 'wetland_bog' },
64802                     { wetland: 'reedbed', pattern: 'wetland_reedbed' },
64803                     { pattern: 'wetland' }
64804                 ],
64805                 wood: [
64806                     { leaf_type: 'broadleaved', pattern: 'forest_broadleaved' },
64807                     { leaf_type: 'needleleaved', pattern: 'forest_needleleaved' },
64808                     { leaf_type: 'leafless', pattern: 'forest_leafless' },
64809                     { pattern: 'forest' } // same as 'leaf_type:mixed'
64810                 ]
64811             },
64812             traffic_calming: {
64813                 island: [
64814                     { surface: 'grass', pattern: 'grass' } ],
64815                 chicane: [
64816                     { surface: 'grass', pattern: 'grass' } ],
64817                 choker: [
64818                     { surface: 'grass', pattern: 'grass' } ]
64819             }
64820         };
64821
64822         function svgTagPattern(tags) {
64823             // Skip pattern filling if this is a building (buildings don't get patterns applied)
64824             if (tags.building && tags.building !== 'no') {
64825                 return null;
64826             }
64827
64828             for (var tag in patterns) {
64829                 var entityValue = tags[tag];
64830                 if (!entityValue) { continue; }
64831
64832                 if (typeof patterns[tag] === 'string') { // extra short syntax (just tag) - pattern name
64833                     return 'pattern-' + patterns[tag];
64834                 } else {
64835                     var values = patterns[tag];
64836                     for (var value in values) {
64837                         if (entityValue !== value) { continue; }
64838
64839                         var rules = values[value];
64840                         if (typeof rules === 'string') { // short syntax - pattern name
64841                             return 'pattern-' + rules;
64842                         }
64843
64844                         // long syntax - rule array
64845                         for (var ruleKey in rules) {
64846                             var rule = rules[ruleKey];
64847
64848                             var pass = true;
64849                             for (var criterion in rule) {
64850                                 if (criterion !== 'pattern') { // reserved for pattern name
64851                                     // The only rule is a required tag-value pair
64852                                     var v = tags[criterion];
64853                                     if (!v || v !== rule[criterion]) {
64854                                         pass = false;
64855                                         break;
64856                                     }
64857                                 }
64858                             }
64859
64860                             if (pass) {
64861                                 return 'pattern-' + rule.pattern;
64862                             }
64863                         }
64864                     }
64865                 }
64866             }
64867
64868             return null;
64869         }
64870
64871         function svgAreas(projection, context) {
64872
64873
64874             function getPatternStyle(tags) {
64875                 var imageID = svgTagPattern(tags);
64876                 if (imageID) {
64877                     return 'url("#ideditor-' + imageID + '")';
64878                 }
64879                 return '';
64880             }
64881
64882
64883             function drawTargets(selection, graph, entities, filter) {
64884                 var targetClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
64885                 var nopeClass = context.getDebug('target') ? 'red ' : 'nocolor ';
64886                 var getPath = svgPath(projection).geojson;
64887                 var activeID = context.activeID();
64888                 var base = context.history().base();
64889
64890                 // The targets and nopes will be MultiLineString sub-segments of the ways
64891                 var data = { targets: [], nopes: [] };
64892
64893                 entities.forEach(function(way) {
64894                     var features = svgSegmentWay(way, graph, activeID);
64895                     data.targets.push.apply(data.targets, features.passive);
64896                     data.nopes.push.apply(data.nopes, features.active);
64897                 });
64898
64899
64900                 // Targets allow hover and vertex snapping
64901                 var targetData = data.targets.filter(getPath);
64902                 var targets = selection.selectAll('.area.target-allowed')
64903                     .filter(function(d) { return filter(d.properties.entity); })
64904                     .data(targetData, function key(d) { return d.id; });
64905
64906                 // exit
64907                 targets.exit()
64908                     .remove();
64909
64910                 var segmentWasEdited = function(d) {
64911                     var wayID = d.properties.entity.id;
64912                     // if the whole line was edited, don't draw segment changes
64913                     if (!base.entities[wayID] ||
64914                         !fastDeepEqual(graph.entities[wayID].nodes, base.entities[wayID].nodes)) {
64915                         return false;
64916                     }
64917                     return d.properties.nodes.some(function(n) {
64918                         return !base.entities[n.id] ||
64919                                !fastDeepEqual(graph.entities[n.id].loc, base.entities[n.id].loc);
64920                     });
64921                 };
64922
64923                 // enter/update
64924                 targets.enter()
64925                     .append('path')
64926                     .merge(targets)
64927                     .attr('d', getPath)
64928                     .attr('class', function(d) { return 'way area target target-allowed ' + targetClass + d.id; })
64929                     .classed('segment-edited', segmentWasEdited);
64930
64931
64932                 // NOPE
64933                 var nopeData = data.nopes.filter(getPath);
64934                 var nopes = selection.selectAll('.area.target-nope')
64935                     .filter(function(d) { return filter(d.properties.entity); })
64936                     .data(nopeData, function key(d) { return d.id; });
64937
64938                 // exit
64939                 nopes.exit()
64940                     .remove();
64941
64942                 // enter/update
64943                 nopes.enter()
64944                     .append('path')
64945                     .merge(nopes)
64946                     .attr('d', getPath)
64947                     .attr('class', function(d) { return 'way area target target-nope ' + nopeClass + d.id; })
64948                     .classed('segment-edited', segmentWasEdited);
64949             }
64950
64951
64952             function drawAreas(selection, graph, entities, filter) {
64953                 var path = svgPath(projection, graph, true);
64954                 var areas = {};
64955                 var multipolygon;
64956                 var base = context.history().base();
64957
64958                 for (var i = 0; i < entities.length; i++) {
64959                     var entity = entities[i];
64960                     if (entity.geometry(graph) !== 'area') { continue; }
64961
64962                     multipolygon = osmIsOldMultipolygonOuterMember(entity, graph);
64963                     if (multipolygon) {
64964                         areas[multipolygon.id] = {
64965                             entity: multipolygon.mergeTags(entity.tags),
64966                             area: Math.abs(entity.area(graph))
64967                         };
64968                     } else if (!areas[entity.id]) {
64969                         areas[entity.id] = {
64970                             entity: entity,
64971                             area: Math.abs(entity.area(graph))
64972                         };
64973                     }
64974                 }
64975
64976                 var fills = Object.values(areas).filter(function hasPath(a) { return path(a.entity); });
64977                 fills.sort(function areaSort(a, b) { return b.area - a.area; });
64978                 fills = fills.map(function(a) { return a.entity; });
64979
64980                 var strokes = fills.filter(function(area) { return area.type === 'way'; });
64981
64982                 var data = {
64983                     clip: fills,
64984                     shadow: strokes,
64985                     stroke: strokes,
64986                     fill: fills
64987                 };
64988
64989                 var clipPaths = context.surface().selectAll('defs').selectAll('.clipPath-osm')
64990                    .filter(filter)
64991                    .data(data.clip, osmEntity.key);
64992
64993                 clipPaths.exit()
64994                    .remove();
64995
64996                 var clipPathsEnter = clipPaths.enter()
64997                    .append('clipPath')
64998                    .attr('class', 'clipPath-osm')
64999                    .attr('id', function(entity) { return 'ideditor-' + entity.id + '-clippath'; });
65000
65001                 clipPathsEnter
65002                    .append('path');
65003
65004                 clipPaths.merge(clipPathsEnter)
65005                    .selectAll('path')
65006                    .attr('d', path);
65007
65008
65009                 var drawLayer = selection.selectAll('.layer-osm.areas');
65010                 var touchLayer = selection.selectAll('.layer-touch.areas');
65011
65012                 // Draw areas..
65013                 var areagroup = drawLayer
65014                     .selectAll('g.areagroup')
65015                     .data(['fill', 'shadow', 'stroke']);
65016
65017                 areagroup = areagroup.enter()
65018                     .append('g')
65019                     .attr('class', function(d) { return 'areagroup area-' + d; })
65020                     .merge(areagroup);
65021
65022                 var paths = areagroup
65023                     .selectAll('path')
65024                     .filter(filter)
65025                     .data(function(layer) { return data[layer]; }, osmEntity.key);
65026
65027                 paths.exit()
65028                     .remove();
65029
65030
65031                 var fillpaths = selection.selectAll('.area-fill path.area').nodes();
65032                 var bisect = d3_bisector(function(node) { return -node.__data__.area(graph); }).left;
65033
65034                 function sortedByArea(entity) {
65035                     if (this._parent.__data__ === 'fill') {
65036                         return fillpaths[bisect(fillpaths, -entity.area(graph))];
65037                     }
65038                 }
65039
65040                 paths = paths.enter()
65041                     .insert('path', sortedByArea)
65042                     .merge(paths)
65043                     .each(function(entity) {
65044                         var layer = this.parentNode.__data__;
65045                         this.setAttribute('class', entity.type + ' area ' + layer + ' ' + entity.id);
65046
65047                         if (layer === 'fill') {
65048                             this.setAttribute('clip-path', 'url(#ideditor-' + entity.id + '-clippath)');
65049                             this.style.fill = this.style.stroke = getPatternStyle(entity.tags);
65050                         }
65051                     })
65052                     .classed('added', function(d) {
65053                         return !base.entities[d.id];
65054                     })
65055                     .classed('geometry-edited', function(d) {
65056                         return graph.entities[d.id] &&
65057                             base.entities[d.id] &&
65058                             !fastDeepEqual(graph.entities[d.id].nodes, base.entities[d.id].nodes);
65059                     })
65060                     .classed('retagged', function(d) {
65061                         return graph.entities[d.id] &&
65062                             base.entities[d.id] &&
65063                             !fastDeepEqual(graph.entities[d.id].tags, base.entities[d.id].tags);
65064                     })
65065                     .call(svgTagClasses())
65066                     .attr('d', path);
65067
65068
65069                 // Draw touch targets..
65070                 touchLayer
65071                     .call(drawTargets, graph, data.stroke, filter);
65072             }
65073
65074             return drawAreas;
65075         }
65076
65077         //[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]
65078         //[4a]          NameChar           ::=          NameStartChar | "-" | "." | [0-9] | #xB7 | [#x0300-#x036F] | [#x203F-#x2040]
65079         //[5]           Name       ::=          NameStartChar (NameChar)*
65080         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
65081         var nameChar = new RegExp("[\\-\\.0-9"+nameStartChar.source.slice(1,-1)+"\\u00B7\\u0300-\\u036F\\u203F-\\u2040]");
65082         var tagNamePattern = new RegExp('^'+nameStartChar.source+nameChar.source+'*(?:\:'+nameStartChar.source+nameChar.source+'*)?$');
65083         //var tagNamePattern = /^[a-zA-Z_][\w\-\.]*(?:\:[a-zA-Z_][\w\-\.]*)?$/
65084         //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(',')
65085
65086         //S_TAG,        S_ATTR, S_EQ,   S_ATTR_NOQUOT_VALUE
65087         //S_ATTR_SPACE, S_ATTR_END,     S_TAG_SPACE, S_TAG_CLOSE
65088         var S_TAG = 0;//tag name offerring
65089         var S_ATTR = 1;//attr name offerring 
65090         var S_ATTR_SPACE=2;//attr name end and space offer
65091         var S_EQ = 3;//=space?
65092         var S_ATTR_NOQUOT_VALUE = 4;//attr value(no quot value only)
65093         var S_ATTR_END = 5;//attr value end and no space(quot end)
65094         var S_TAG_SPACE = 6;//(attr value end || tag end ) && (space offer)
65095         var S_TAG_CLOSE = 7;//closed el<el />
65096
65097         function XMLReader(){
65098                 
65099         }
65100
65101         XMLReader.prototype = {
65102                 parse:function(source,defaultNSMap,entityMap){
65103                         var domBuilder = this.domBuilder;
65104                         domBuilder.startDocument();
65105                         _copy(defaultNSMap ,defaultNSMap = {});
65106                         parse(source,defaultNSMap,entityMap,
65107                                         domBuilder,this.errorHandler);
65108                         domBuilder.endDocument();
65109                 }
65110         };
65111         function parse(source,defaultNSMapCopy,entityMap,domBuilder,errorHandler){
65112                 function fixedFromCharCode(code) {
65113                         // String.prototype.fromCharCode does not supports
65114                         // > 2 bytes unicode chars directly
65115                         if (code > 0xffff) {
65116                                 code -= 0x10000;
65117                                 var surrogate1 = 0xd800 + (code >> 10)
65118                                         , surrogate2 = 0xdc00 + (code & 0x3ff);
65119
65120                                 return String.fromCharCode(surrogate1, surrogate2);
65121                         } else {
65122                                 return String.fromCharCode(code);
65123                         }
65124                 }
65125                 function entityReplacer(a){
65126                         var k = a.slice(1,-1);
65127                         if(k in entityMap){
65128                                 return entityMap[k]; 
65129                         }else if(k.charAt(0) === '#'){
65130                                 return fixedFromCharCode(parseInt(k.substr(1).replace('x','0x')))
65131                         }else {
65132                                 errorHandler.error('entity not found:'+a);
65133                                 return a;
65134                         }
65135                 }
65136                 function appendText(end){//has some bugs
65137                         if(end>start){
65138                                 var xt = source.substring(start,end).replace(/&#?\w+;/g,entityReplacer);
65139                                 locator&&position(start);
65140                                 domBuilder.characters(xt,0,end-start);
65141                                 start = end;
65142                         }
65143                 }
65144                 function position(p,m){
65145                         while(p>=lineEnd && (m = linePattern.exec(source))){
65146                                 lineStart = m.index;
65147                                 lineEnd = lineStart + m[0].length;
65148                                 locator.lineNumber++;
65149                                 //console.log('line++:',locator,startPos,endPos)
65150                         }
65151                         locator.columnNumber = p-lineStart+1;
65152                 }
65153                 var lineStart = 0;
65154                 var lineEnd = 0;
65155                 var linePattern = /.*(?:\r\n?|\n)|.*$/g;
65156                 var locator = domBuilder.locator;
65157                 
65158                 var parseStack = [{currentNSMap:defaultNSMapCopy}];
65159                 var closeMap = {};
65160                 var start = 0;
65161                 while(true){
65162                         try{
65163                                 var tagStart = source.indexOf('<',start);
65164                                 if(tagStart<0){
65165                                         if(!source.substr(start).match(/^\s*$/)){
65166                                                 var doc = domBuilder.doc;
65167                                         var text = doc.createTextNode(source.substr(start));
65168                                         doc.appendChild(text);
65169                                         domBuilder.currentElement = text;
65170                                         }
65171                                         return;
65172                                 }
65173                                 if(tagStart>start){
65174                                         appendText(tagStart);
65175                                 }
65176                                 switch(source.charAt(tagStart+1)){
65177                                 case '/':
65178                                         var end = source.indexOf('>',tagStart+3);
65179                                         var tagName = source.substring(tagStart+2,end);
65180                                         var config = parseStack.pop();
65181                                         if(end<0){
65182                                                 
65183                                         tagName = source.substring(tagStart+2).replace(/[\s<].*/,'');
65184                                         //console.error('#@@@@@@'+tagName)
65185                                         errorHandler.error("end tag name: "+tagName+' is not complete:'+config.tagName);
65186                                         end = tagStart+1+tagName.length;
65187                                 }else if(tagName.match(/\s</)){
65188                                         tagName = tagName.replace(/[\s<].*/,'');
65189                                         errorHandler.error("end tag name: "+tagName+' maybe not complete');
65190                                         end = tagStart+1+tagName.length;
65191                                         }
65192                                         //console.error(parseStack.length,parseStack)
65193                                         //console.error(config);
65194                                         var localNSMap = config.localNSMap;
65195                                         var endMatch = config.tagName == tagName;
65196                                         var endIgnoreCaseMach = endMatch || config.tagName&&config.tagName.toLowerCase() == tagName.toLowerCase();
65197                                 if(endIgnoreCaseMach){
65198                                         domBuilder.endElement(config.uri,config.localName,tagName);
65199                                                 if(localNSMap){
65200                                                         for(var prefix in localNSMap){
65201                                                                 domBuilder.endPrefixMapping(prefix) ;
65202                                                         }
65203                                                 }
65204                                                 if(!endMatch){
65205                                         errorHandler.fatalError("end tag name: "+tagName+' is not match the current start tagName:'+config.tagName );
65206                                                 }
65207                                 }else {
65208                                         parseStack.push(config);
65209                                 }
65210                                         
65211                                         end++;
65212                                         break;
65213                                         // end elment
65214                                 case '?':// <?...?>
65215                                         locator&&position(tagStart);
65216                                         end = parseInstruction(source,tagStart,domBuilder);
65217                                         break;
65218                                 case '!':// <!doctype,<![CDATA,<!--
65219                                         locator&&position(tagStart);
65220                                         end = parseDCC(source,tagStart,domBuilder,errorHandler);
65221                                         break;
65222                                 default:
65223                                         locator&&position(tagStart);
65224                                         var el = new ElementAttributes();
65225                                         var currentNSMap = parseStack[parseStack.length-1].currentNSMap;
65226                                         //elStartEnd
65227                                         var end = parseElementStartPart(source,tagStart,el,currentNSMap,entityReplacer,errorHandler);
65228                                         var len = el.length;
65229                                         
65230                                         
65231                                         if(!el.closed && fixSelfClosed(source,end,el.tagName,closeMap)){
65232                                                 el.closed = true;
65233                                                 if(!entityMap.nbsp){
65234                                                         errorHandler.warning('unclosed xml attribute');
65235                                                 }
65236                                         }
65237                                         if(locator && len){
65238                                                 var locator2 = copyLocator(locator,{});
65239                                                 //try{//attribute position fixed
65240                                                 for(var i = 0;i<len;i++){
65241                                                         var a = el[i];
65242                                                         position(a.offset);
65243                                                         a.locator = copyLocator(locator,{});
65244                                                 }
65245                                                 //}catch(e){console.error('@@@@@'+e)}
65246                                                 domBuilder.locator = locator2;
65247                                                 if(appendElement(el,domBuilder,currentNSMap)){
65248                                                         parseStack.push(el);
65249                                                 }
65250                                                 domBuilder.locator = locator;
65251                                         }else {
65252                                                 if(appendElement(el,domBuilder,currentNSMap)){
65253                                                         parseStack.push(el);
65254                                                 }
65255                                         }
65256                                         
65257                                         
65258                                         
65259                                         if(el.uri === 'http://www.w3.org/1999/xhtml' && !el.closed){
65260                                                 end = parseHtmlSpecialContent(source,end,el.tagName,entityReplacer,domBuilder);
65261                                         }else {
65262                                                 end++;
65263                                         }
65264                                 }
65265                         }catch(e){
65266                                 errorHandler.error('element parse error: '+e);
65267                                 //errorHandler.error('element parse error: '+e);
65268                                 end = -1;
65269                                 //throw e;
65270                         }
65271                         if(end>start){
65272                                 start = end;
65273                         }else {
65274                                 //TODO: 这里有可能sax回退,有位置错误风险
65275                                 appendText(Math.max(tagStart,start)+1);
65276                         }
65277                 }
65278         }
65279         function copyLocator(f,t){
65280                 t.lineNumber = f.lineNumber;
65281                 t.columnNumber = f.columnNumber;
65282                 return t;
65283         }
65284
65285         /**
65286          * @see #appendElement(source,elStartEnd,el,selfClosed,entityReplacer,domBuilder,parseStack);
65287          * @return end of the elementStartPart(end of elementEndPart for selfClosed el)
65288          */
65289         function parseElementStartPart(source,start,el,currentNSMap,entityReplacer,errorHandler){
65290                 var attrName;
65291                 var value;
65292                 var p = ++start;
65293                 var s = S_TAG;//status
65294                 while(true){
65295                         var c = source.charAt(p);
65296                         switch(c){
65297                         case '=':
65298                                 if(s === S_ATTR){//attrName
65299                                         attrName = source.slice(start,p);
65300                                         s = S_EQ;
65301                                 }else if(s === S_ATTR_SPACE){
65302                                         s = S_EQ;
65303                                 }else {
65304                                         //fatalError: equal must after attrName or space after attrName
65305                                         throw new Error('attribute equal must after attrName');
65306                                 }
65307                                 break;
65308                         case '\'':
65309                         case '"':
65310                                 if(s === S_EQ || s === S_ATTR //|| s == S_ATTR_SPACE
65311                                         ){//equal
65312                                         if(s === S_ATTR){
65313                                                 errorHandler.warning('attribute value must after "="');
65314                                                 attrName = source.slice(start,p);
65315                                         }
65316                                         start = p+1;
65317                                         p = source.indexOf(c,start);
65318                                         if(p>0){
65319                                                 value = source.slice(start,p).replace(/&#?\w+;/g,entityReplacer);
65320                                                 el.add(attrName,value,start-1);
65321                                                 s = S_ATTR_END;
65322                                         }else {
65323                                                 //fatalError: no end quot match
65324                                                 throw new Error('attribute value no end \''+c+'\' match');
65325                                         }
65326                                 }else if(s == S_ATTR_NOQUOT_VALUE){
65327                                         value = source.slice(start,p).replace(/&#?\w+;/g,entityReplacer);
65328                                         //console.log(attrName,value,start,p)
65329                                         el.add(attrName,value,start);
65330                                         //console.dir(el)
65331                                         errorHandler.warning('attribute "'+attrName+'" missed start quot('+c+')!!');
65332                                         start = p+1;
65333                                         s = S_ATTR_END;
65334                                 }else {
65335                                         //fatalError: no equal before
65336                                         throw new Error('attribute value must after "="');
65337                                 }
65338                                 break;
65339                         case '/':
65340                                 switch(s){
65341                                 case S_TAG:
65342                                         el.setTagName(source.slice(start,p));
65343                                 case S_ATTR_END:
65344                                 case S_TAG_SPACE:
65345                                 case S_TAG_CLOSE:
65346                                         s =S_TAG_CLOSE;
65347                                         el.closed = true;
65348                                 case S_ATTR_NOQUOT_VALUE:
65349                                 case S_ATTR:
65350                                 case S_ATTR_SPACE:
65351                                         break;
65352                                 //case S_EQ:
65353                                 default:
65354                                         throw new Error("attribute invalid close char('/')")
65355                                 }
65356                                 break;
65357                         case ''://end document
65358                                 //throw new Error('unexpected end of input')
65359                                 errorHandler.error('unexpected end of input');
65360                                 if(s == S_TAG){
65361                                         el.setTagName(source.slice(start,p));
65362                                 }
65363                                 return p;
65364                         case '>':
65365                                 switch(s){
65366                                 case S_TAG:
65367                                         el.setTagName(source.slice(start,p));
65368                                 case S_ATTR_END:
65369                                 case S_TAG_SPACE:
65370                                 case S_TAG_CLOSE:
65371                                         break;//normal
65372                                 case S_ATTR_NOQUOT_VALUE://Compatible state
65373                                 case S_ATTR:
65374                                         value = source.slice(start,p);
65375                                         if(value.slice(-1) === '/'){
65376                                                 el.closed  = true;
65377                                                 value = value.slice(0,-1);
65378                                         }
65379                                 case S_ATTR_SPACE:
65380                                         if(s === S_ATTR_SPACE){
65381                                                 value = attrName;
65382                                         }
65383                                         if(s == S_ATTR_NOQUOT_VALUE){
65384                                                 errorHandler.warning('attribute "'+value+'" missed quot(")!!');
65385                                                 el.add(attrName,value.replace(/&#?\w+;/g,entityReplacer),start);
65386                                         }else {
65387                                                 if(currentNSMap[''] !== 'http://www.w3.org/1999/xhtml' || !value.match(/^(?:disabled|checked|selected)$/i)){
65388                                                         errorHandler.warning('attribute "'+value+'" missed value!! "'+value+'" instead!!');
65389                                                 }
65390                                                 el.add(value,value,start);
65391                                         }
65392                                         break;
65393                                 case S_EQ:
65394                                         throw new Error('attribute value missed!!');
65395                                 }
65396         //                      console.log(tagName,tagNamePattern,tagNamePattern.test(tagName))
65397                                 return p;
65398                         /*xml space '\x20' | #x9 | #xD | #xA; */
65399                         case '\u0080':
65400                                 c = ' ';
65401                         default:
65402                                 if(c<= ' '){//space
65403                                         switch(s){
65404                                         case S_TAG:
65405                                                 el.setTagName(source.slice(start,p));//tagName
65406                                                 s = S_TAG_SPACE;
65407                                                 break;
65408                                         case S_ATTR:
65409                                                 attrName = source.slice(start,p);
65410                                                 s = S_ATTR_SPACE;
65411                                                 break;
65412                                         case S_ATTR_NOQUOT_VALUE:
65413                                                 var value = source.slice(start,p).replace(/&#?\w+;/g,entityReplacer);
65414                                                 errorHandler.warning('attribute "'+value+'" missed quot(")!!');
65415                                                 el.add(attrName,value,start);
65416                                         case S_ATTR_END:
65417                                                 s = S_TAG_SPACE;
65418                                                 break;
65419                                         //case S_TAG_SPACE:
65420                                         //case S_EQ:
65421                                         //case S_ATTR_SPACE:
65422                                         //      void();break;
65423                                         //case S_TAG_CLOSE:
65424                                                 //ignore warning
65425                                         }
65426                                 }else {//not space
65427         //S_TAG,        S_ATTR, S_EQ,   S_ATTR_NOQUOT_VALUE
65428         //S_ATTR_SPACE, S_ATTR_END,     S_TAG_SPACE, S_TAG_CLOSE
65429                                         switch(s){
65430                                         //case S_TAG:void();break;
65431                                         //case S_ATTR:void();break;
65432                                         //case S_ATTR_NOQUOT_VALUE:void();break;
65433                                         case S_ATTR_SPACE:
65434                                                 var tagName =  el.tagName;
65435                                                 if(currentNSMap[''] !== 'http://www.w3.org/1999/xhtml' || !attrName.match(/^(?:disabled|checked|selected)$/i)){
65436                                                         errorHandler.warning('attribute "'+attrName+'" missed value!! "'+attrName+'" instead2!!');
65437                                                 }
65438                                                 el.add(attrName,attrName,start);
65439                                                 start = p;
65440                                                 s = S_ATTR;
65441                                                 break;
65442                                         case S_ATTR_END:
65443                                                 errorHandler.warning('attribute space is required"'+attrName+'"!!');
65444                                         case S_TAG_SPACE:
65445                                                 s = S_ATTR;
65446                                                 start = p;
65447                                                 break;
65448                                         case S_EQ:
65449                                                 s = S_ATTR_NOQUOT_VALUE;
65450                                                 start = p;
65451                                                 break;
65452                                         case S_TAG_CLOSE:
65453                                                 throw new Error("elements closed character '/' and '>' must be connected to");
65454                                         }
65455                                 }
65456                         }//end outer switch
65457                         //console.log('p++',p)
65458                         p++;
65459                 }
65460         }
65461         /**
65462          * @return true if has new namespace define
65463          */
65464         function appendElement(el,domBuilder,currentNSMap){
65465                 var tagName = el.tagName;
65466                 var localNSMap = null;
65467                 //var currentNSMap = parseStack[parseStack.length-1].currentNSMap;
65468                 var i = el.length;
65469                 while(i--){
65470                         var a = el[i];
65471                         var qName = a.qName;
65472                         var value = a.value;
65473                         var nsp = qName.indexOf(':');
65474                         if(nsp>0){
65475                                 var prefix = a.prefix = qName.slice(0,nsp);
65476                                 var localName = qName.slice(nsp+1);
65477                                 var nsPrefix = prefix === 'xmlns' && localName;
65478                         }else {
65479                                 localName = qName;
65480                                 prefix = null;
65481                                 nsPrefix = qName === 'xmlns' && '';
65482                         }
65483                         //can not set prefix,because prefix !== ''
65484                         a.localName = localName ;
65485                         //prefix == null for no ns prefix attribute 
65486                         if(nsPrefix !== false){//hack!!
65487                                 if(localNSMap == null){
65488                                         localNSMap = {};
65489                                         //console.log(currentNSMap,0)
65490                                         _copy(currentNSMap,currentNSMap={});
65491                                         //console.log(currentNSMap,1)
65492                                 }
65493                                 currentNSMap[nsPrefix] = localNSMap[nsPrefix] = value;
65494                                 a.uri = 'http://www.w3.org/2000/xmlns/';
65495                                 domBuilder.startPrefixMapping(nsPrefix, value); 
65496                         }
65497                 }
65498                 var i = el.length;
65499                 while(i--){
65500                         a = el[i];
65501                         var prefix = a.prefix;
65502                         if(prefix){//no prefix attribute has no namespace
65503                                 if(prefix === 'xml'){
65504                                         a.uri = 'http://www.w3.org/XML/1998/namespace';
65505                                 }if(prefix !== 'xmlns'){
65506                                         a.uri = currentNSMap[prefix || ''];
65507                                         
65508                                         //{console.log('###'+a.qName,domBuilder.locator.systemId+'',currentNSMap,a.uri)}
65509                                 }
65510                         }
65511                 }
65512                 var nsp = tagName.indexOf(':');
65513                 if(nsp>0){
65514                         prefix = el.prefix = tagName.slice(0,nsp);
65515                         localName = el.localName = tagName.slice(nsp+1);
65516                 }else {
65517                         prefix = null;//important!!
65518                         localName = el.localName = tagName;
65519                 }
65520                 //no prefix element has default namespace
65521                 var ns = el.uri = currentNSMap[prefix || ''];
65522                 domBuilder.startElement(ns,localName,tagName,el);
65523                 //endPrefixMapping and startPrefixMapping have not any help for dom builder
65524                 //localNSMap = null
65525                 if(el.closed){
65526                         domBuilder.endElement(ns,localName,tagName);
65527                         if(localNSMap){
65528                                 for(prefix in localNSMap){
65529                                         domBuilder.endPrefixMapping(prefix); 
65530                                 }
65531                         }
65532                 }else {
65533                         el.currentNSMap = currentNSMap;
65534                         el.localNSMap = localNSMap;
65535                         //parseStack.push(el);
65536                         return true;
65537                 }
65538         }
65539         function parseHtmlSpecialContent(source,elStartEnd,tagName,entityReplacer,domBuilder){
65540                 if(/^(?:script|textarea)$/i.test(tagName)){
65541                         var elEndStart =  source.indexOf('</'+tagName+'>',elStartEnd);
65542                         var text = source.substring(elStartEnd+1,elEndStart);
65543                         if(/[&<]/.test(text)){
65544                                 if(/^script$/i.test(tagName)){
65545                                         //if(!/\]\]>/.test(text)){
65546                                                 //lexHandler.startCDATA();
65547                                                 domBuilder.characters(text,0,text.length);
65548                                                 //lexHandler.endCDATA();
65549                                                 return elEndStart;
65550                                         //}
65551                                 }//}else{//text area
65552                                         text = text.replace(/&#?\w+;/g,entityReplacer);
65553                                         domBuilder.characters(text,0,text.length);
65554                                         return elEndStart;
65555                                 //}
65556                                 
65557                         }
65558                 }
65559                 return elStartEnd+1;
65560         }
65561         function fixSelfClosed(source,elStartEnd,tagName,closeMap){
65562                 //if(tagName in closeMap){
65563                 var pos = closeMap[tagName];
65564                 if(pos == null){
65565                         //console.log(tagName)
65566                         pos =  source.lastIndexOf('</'+tagName+'>');
65567                         if(pos<elStartEnd){//忘记闭合
65568                                 pos = source.lastIndexOf('</'+tagName);
65569                         }
65570                         closeMap[tagName] =pos;
65571                 }
65572                 return pos<elStartEnd;
65573                 //} 
65574         }
65575         function _copy(source,target){
65576                 for(var n in source){target[n] = source[n];}
65577         }
65578         function parseDCC(source,start,domBuilder,errorHandler){//sure start with '<!'
65579                 var next= source.charAt(start+2);
65580                 switch(next){
65581                 case '-':
65582                         if(source.charAt(start + 3) === '-'){
65583                                 var end = source.indexOf('-->',start+4);
65584                                 //append comment source.substring(4,end)//<!--
65585                                 if(end>start){
65586                                         domBuilder.comment(source,start+4,end-start-4);
65587                                         return end+3;
65588                                 }else {
65589                                         errorHandler.error("Unclosed comment");
65590                                         return -1;
65591                                 }
65592                         }else {
65593                                 //error
65594                                 return -1;
65595                         }
65596                 default:
65597                         if(source.substr(start+3,6) == 'CDATA['){
65598                                 var end = source.indexOf(']]>',start+9);
65599                                 domBuilder.startCDATA();
65600                                 domBuilder.characters(source,start+9,end-start-9);
65601                                 domBuilder.endCDATA(); 
65602                                 return end+3;
65603                         }
65604                         //<!DOCTYPE
65605                         //startDTD(java.lang.String name, java.lang.String publicId, java.lang.String systemId) 
65606                         var matchs = split(source,start);
65607                         var len = matchs.length;
65608                         if(len>1 && /!doctype/i.test(matchs[0][0])){
65609                                 var name = matchs[1][0];
65610                                 var pubid = len>3 && /^public$/i.test(matchs[2][0]) && matchs[3][0];
65611                                 var sysid = len>4 && matchs[4][0];
65612                                 var lastMatch = matchs[len-1];
65613                                 domBuilder.startDTD(name,pubid && pubid.replace(/^(['"])(.*?)\1$/,'$2'),
65614                                                 sysid && sysid.replace(/^(['"])(.*?)\1$/,'$2'));
65615                                 domBuilder.endDTD();
65616                                 
65617                                 return lastMatch.index+lastMatch[0].length
65618                         }
65619                 }
65620                 return -1;
65621         }
65622
65623
65624
65625         function parseInstruction(source,start,domBuilder){
65626                 var end = source.indexOf('?>',start);
65627                 if(end){
65628                         var match = source.substring(start,end).match(/^<\?(\S*)\s*([\s\S]*?)\s*$/);
65629                         if(match){
65630                                 var len = match[0].length;
65631                                 domBuilder.processingInstruction(match[1], match[2]) ;
65632                                 return end+2;
65633                         }else {//error
65634                                 return -1;
65635                         }
65636                 }
65637                 return -1;
65638         }
65639
65640         /**
65641          * @param source
65642          */
65643         function ElementAttributes(source){
65644                 
65645         }
65646         ElementAttributes.prototype = {
65647                 setTagName:function(tagName){
65648                         if(!tagNamePattern.test(tagName)){
65649                                 throw new Error('invalid tagName:'+tagName)
65650                         }
65651                         this.tagName = tagName;
65652                 },
65653                 add:function(qName,value,offset){
65654                         if(!tagNamePattern.test(qName)){
65655                                 throw new Error('invalid attribute:'+qName)
65656                         }
65657                         this[this.length++] = {qName:qName,value:value,offset:offset};
65658                 },
65659                 length:0,
65660                 getLocalName:function(i){return this[i].localName},
65661                 getLocator:function(i){return this[i].locator},
65662                 getQName:function(i){return this[i].qName},
65663                 getURI:function(i){return this[i].uri},
65664                 getValue:function(i){return this[i].value}
65665         //      ,getIndex:function(uri, localName)){
65666         //              if(localName){
65667         //                      
65668         //              }else{
65669         //                      var qName = uri
65670         //              }
65671         //      },
65672         //      getValue:function(){return this.getValue(this.getIndex.apply(this,arguments))},
65673         //      getType:function(uri,localName){}
65674         //      getType:function(i){},
65675         };
65676
65677
65678
65679
65680         function _set_proto_(thiz,parent){
65681                 thiz.__proto__ = parent;
65682                 return thiz;
65683         }
65684         if(!(_set_proto_({},_set_proto_.prototype) instanceof _set_proto_)){
65685                 _set_proto_ = function(thiz,parent){
65686                         function p(){}          p.prototype = parent;
65687                         p = new p();
65688                         for(parent in thiz){
65689                                 p[parent] = thiz[parent];
65690                         }
65691                         return p;
65692                 };
65693         }
65694
65695         function split(source,start){
65696                 var match;
65697                 var buf = [];
65698                 var reg = /'[^']+'|"[^"]+"|[^\s<>\/=]+=?|(\/?\s*>|<)/g;
65699                 reg.lastIndex = start;
65700                 reg.exec(source);//skip <
65701                 while(match = reg.exec(source)){
65702                         buf.push(match);
65703                         if(match[1]){ return buf; }
65704                 }
65705         }
65706
65707         var XMLReader_1 = XMLReader;
65708
65709         var sax = {
65710                 XMLReader: XMLReader_1
65711         };
65712
65713         /*
65714          * DOM Level 2
65715          * Object DOMException
65716          * @see http://www.w3.org/TR/REC-DOM-Level-1/ecma-script-language-binding.html
65717          * @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/ecma-script-binding.html
65718          */
65719
65720         function copy$2(src,dest){
65721                 for(var p in src){
65722                         dest[p] = src[p];
65723                 }
65724         }
65725         /**
65726         ^\w+\.prototype\.([_\w]+)\s*=\s*((?:.*\{\s*?[\r\n][\s\S]*?^})|\S.*?(?=[;\r\n]));?
65727         ^\w+\.prototype\.([_\w]+)\s*=\s*(\S.*?(?=[;\r\n]));?
65728          */
65729         function _extends(Class,Super){
65730                 var pt = Class.prototype;
65731                 if(Object.create){
65732                         var ppt = Object.create(Super.prototype);
65733                         pt.__proto__ = ppt;
65734                 }
65735                 if(!(pt instanceof Super)){
65736                         function t(){}          t.prototype = Super.prototype;
65737                         t = new t();
65738                         copy$2(pt,t);
65739                         Class.prototype = pt = t;
65740                 }
65741                 if(pt.constructor != Class){
65742                         if(typeof Class != 'function'){
65743                                 console.error("unknow Class:"+Class);
65744                         }
65745                         pt.constructor = Class;
65746                 }
65747         }
65748         var htmlns = 'http://www.w3.org/1999/xhtml' ;
65749         // Node Types
65750         var NodeType = {};
65751         var ELEMENT_NODE                = NodeType.ELEMENT_NODE                = 1;
65752         var ATTRIBUTE_NODE              = NodeType.ATTRIBUTE_NODE              = 2;
65753         var TEXT_NODE                   = NodeType.TEXT_NODE                   = 3;
65754         var CDATA_SECTION_NODE          = NodeType.CDATA_SECTION_NODE          = 4;
65755         var ENTITY_REFERENCE_NODE       = NodeType.ENTITY_REFERENCE_NODE       = 5;
65756         var ENTITY_NODE                 = NodeType.ENTITY_NODE                 = 6;
65757         var PROCESSING_INSTRUCTION_NODE = NodeType.PROCESSING_INSTRUCTION_NODE = 7;
65758         var COMMENT_NODE                = NodeType.COMMENT_NODE                = 8;
65759         var DOCUMENT_NODE               = NodeType.DOCUMENT_NODE               = 9;
65760         var DOCUMENT_TYPE_NODE          = NodeType.DOCUMENT_TYPE_NODE          = 10;
65761         var DOCUMENT_FRAGMENT_NODE      = NodeType.DOCUMENT_FRAGMENT_NODE      = 11;
65762         var NOTATION_NODE               = NodeType.NOTATION_NODE               = 12;
65763
65764         // ExceptionCode
65765         var ExceptionCode = {};
65766         var ExceptionMessage = {};
65767         var INDEX_SIZE_ERR              = ExceptionCode.INDEX_SIZE_ERR              = ((ExceptionMessage[1]="Index size error"),1);
65768         var DOMSTRING_SIZE_ERR          = ExceptionCode.DOMSTRING_SIZE_ERR          = ((ExceptionMessage[2]="DOMString size error"),2);
65769         var HIERARCHY_REQUEST_ERR       = ExceptionCode.HIERARCHY_REQUEST_ERR       = ((ExceptionMessage[3]="Hierarchy request error"),3);
65770         var WRONG_DOCUMENT_ERR          = ExceptionCode.WRONG_DOCUMENT_ERR          = ((ExceptionMessage[4]="Wrong document"),4);
65771         var INVALID_CHARACTER_ERR       = ExceptionCode.INVALID_CHARACTER_ERR       = ((ExceptionMessage[5]="Invalid character"),5);
65772         var NO_DATA_ALLOWED_ERR         = ExceptionCode.NO_DATA_ALLOWED_ERR         = ((ExceptionMessage[6]="No data allowed"),6);
65773         var NO_MODIFICATION_ALLOWED_ERR = ExceptionCode.NO_MODIFICATION_ALLOWED_ERR = ((ExceptionMessage[7]="No modification allowed"),7);
65774         var NOT_FOUND_ERR               = ExceptionCode.NOT_FOUND_ERR               = ((ExceptionMessage[8]="Not found"),8);
65775         var NOT_SUPPORTED_ERR           = ExceptionCode.NOT_SUPPORTED_ERR           = ((ExceptionMessage[9]="Not supported"),9);
65776         var INUSE_ATTRIBUTE_ERR         = ExceptionCode.INUSE_ATTRIBUTE_ERR         = ((ExceptionMessage[10]="Attribute in use"),10);
65777         //level2
65778         var INVALID_STATE_ERR           = ExceptionCode.INVALID_STATE_ERR               = ((ExceptionMessage[11]="Invalid state"),11);
65779         var SYNTAX_ERR                  = ExceptionCode.SYNTAX_ERR                      = ((ExceptionMessage[12]="Syntax error"),12);
65780         var INVALID_MODIFICATION_ERR    = ExceptionCode.INVALID_MODIFICATION_ERR        = ((ExceptionMessage[13]="Invalid modification"),13);
65781         var NAMESPACE_ERR               = ExceptionCode.NAMESPACE_ERR                   = ((ExceptionMessage[14]="Invalid namespace"),14);
65782         var INVALID_ACCESS_ERR          = ExceptionCode.INVALID_ACCESS_ERR              = ((ExceptionMessage[15]="Invalid access"),15);
65783
65784
65785         function DOMException$2(code, message) {
65786                 if(message instanceof Error){
65787                         var error = message;
65788                 }else {
65789                         error = this;
65790                         Error.call(this, ExceptionMessage[code]);
65791                         this.message = ExceptionMessage[code];
65792                         if(Error.captureStackTrace) { Error.captureStackTrace(this, DOMException$2); }
65793                 }
65794                 error.code = code;
65795                 if(message) { this.message = this.message + ": " + message; }
65796                 return error;
65797         }DOMException$2.prototype = Error.prototype;
65798         copy$2(ExceptionCode,DOMException$2);
65799         /**
65800          * @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-536297177
65801          * 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.
65802          * The items in the NodeList are accessible via an integral index, starting from 0.
65803          */
65804         function NodeList() {
65805         }NodeList.prototype = {
65806                 /**
65807                  * The number of nodes in the list. The range of valid child node indices is 0 to length-1 inclusive.
65808                  * @standard level1
65809                  */
65810                 length:0, 
65811                 /**
65812                  * 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.
65813                  * @standard level1
65814                  * @param index  unsigned long 
65815                  *   Index into the collection.
65816                  * @return Node
65817                  *      The node at the indexth position in the NodeList, or null if that is not a valid index. 
65818                  */
65819                 item: function(index) {
65820                         return this[index] || null;
65821                 },
65822                 toString:function(isHTML,nodeFilter){
65823                         for(var buf = [], i = 0;i<this.length;i++){
65824                                 serializeToString(this[i],buf,isHTML,nodeFilter);
65825                         }
65826                         return buf.join('');
65827                 }
65828         };
65829         function LiveNodeList(node,refresh){
65830                 this._node = node;
65831                 this._refresh = refresh;
65832                 _updateLiveList(this);
65833         }
65834         function _updateLiveList(list){
65835                 var inc = list._node._inc || list._node.ownerDocument._inc;
65836                 if(list._inc != inc){
65837                         var ls = list._refresh(list._node);
65838                         //console.log(ls.length)
65839                         __set__(list,'length',ls.length);
65840                         copy$2(ls,list);
65841                         list._inc = inc;
65842                 }
65843         }
65844         LiveNodeList.prototype.item = function(i){
65845                 _updateLiveList(this);
65846                 return this[i];
65847         };
65848
65849         _extends(LiveNodeList,NodeList);
65850         /**
65851          * 
65852          * 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.
65853          * NamedNodeMap objects in the DOM are live.
65854          * used for attributes or DocumentType entities 
65855          */
65856         function NamedNodeMap() {
65857         }
65858         function _findNodeIndex(list,node){
65859                 var i = list.length;
65860                 while(i--){
65861                         if(list[i] === node){return i}
65862                 }
65863         }
65864
65865         function _addNamedNode(el,list,newAttr,oldAttr){
65866                 if(oldAttr){
65867                         list[_findNodeIndex(list,oldAttr)] = newAttr;
65868                 }else {
65869                         list[list.length++] = newAttr;
65870                 }
65871                 if(el){
65872                         newAttr.ownerElement = el;
65873                         var doc = el.ownerDocument;
65874                         if(doc){
65875                                 oldAttr && _onRemoveAttribute(doc,el,oldAttr);
65876                                 _onAddAttribute(doc,el,newAttr);
65877                         }
65878                 }
65879         }
65880         function _removeNamedNode(el,list,attr){
65881                 //console.log('remove attr:'+attr)
65882                 var i = _findNodeIndex(list,attr);
65883                 if(i>=0){
65884                         var lastIndex = list.length-1;
65885                         while(i<lastIndex){
65886                                 list[i] = list[++i];
65887                         }
65888                         list.length = lastIndex;
65889                         if(el){
65890                                 var doc = el.ownerDocument;
65891                                 if(doc){
65892                                         _onRemoveAttribute(doc,el,attr);
65893                                         attr.ownerElement = null;
65894                                 }
65895                         }
65896                 }else {
65897                         throw DOMException$2(NOT_FOUND_ERR,new Error(el.tagName+'@'+attr))
65898                 }
65899         }
65900         NamedNodeMap.prototype = {
65901                 length:0,
65902                 item:NodeList.prototype.item,
65903                 getNamedItem: function(key) {
65904         //              if(key.indexOf(':')>0 || key == 'xmlns'){
65905         //                      return null;
65906         //              }
65907                         //console.log()
65908                         var i = this.length;
65909                         while(i--){
65910                                 var attr = this[i];
65911                                 //console.log(attr.nodeName,key)
65912                                 if(attr.nodeName == key){
65913                                         return attr;
65914                                 }
65915                         }
65916                 },
65917                 setNamedItem: function(attr) {
65918                         var el = attr.ownerElement;
65919                         if(el && el!=this._ownerElement){
65920                                 throw new DOMException$2(INUSE_ATTRIBUTE_ERR);
65921                         }
65922                         var oldAttr = this.getNamedItem(attr.nodeName);
65923                         _addNamedNode(this._ownerElement,this,attr,oldAttr);
65924                         return oldAttr;
65925                 },
65926                 /* returns Node */
65927                 setNamedItemNS: function(attr) {// raises: WRONG_DOCUMENT_ERR,NO_MODIFICATION_ALLOWED_ERR,INUSE_ATTRIBUTE_ERR
65928                         var el = attr.ownerElement, oldAttr;
65929                         if(el && el!=this._ownerElement){
65930                                 throw new DOMException$2(INUSE_ATTRIBUTE_ERR);
65931                         }
65932                         oldAttr = this.getNamedItemNS(attr.namespaceURI,attr.localName);
65933                         _addNamedNode(this._ownerElement,this,attr,oldAttr);
65934                         return oldAttr;
65935                 },
65936
65937                 /* returns Node */
65938                 removeNamedItem: function(key) {
65939                         var attr = this.getNamedItem(key);
65940                         _removeNamedNode(this._ownerElement,this,attr);
65941                         return attr;
65942                         
65943                         
65944                 },// raises: NOT_FOUND_ERR,NO_MODIFICATION_ALLOWED_ERR
65945                 
65946                 //for level2
65947                 removeNamedItemNS:function(namespaceURI,localName){
65948                         var attr = this.getNamedItemNS(namespaceURI,localName);
65949                         _removeNamedNode(this._ownerElement,this,attr);
65950                         return attr;
65951                 },
65952                 getNamedItemNS: function(namespaceURI, localName) {
65953                         var i = this.length;
65954                         while(i--){
65955                                 var node = this[i];
65956                                 if(node.localName == localName && node.namespaceURI == namespaceURI){
65957                                         return node;
65958                                 }
65959                         }
65960                         return null;
65961                 }
65962         };
65963         /**
65964          * @see http://www.w3.org/TR/REC-DOM-Level-1/level-one-core.html#ID-102161490
65965          */
65966         function DOMImplementation(/* Object */ features) {
65967                 this._features = {};
65968                 if (features) {
65969                         for (var feature in features) {
65970                                  this._features = features[feature];
65971                         }
65972                 }
65973         }
65974         DOMImplementation.prototype = {
65975                 hasFeature: function(/* string */ feature, /* string */ version) {
65976                         var versions = this._features[feature.toLowerCase()];
65977                         if (versions && (!version || version in versions)) {
65978                                 return true;
65979                         } else {
65980                                 return false;
65981                         }
65982                 },
65983                 // Introduced in DOM Level 2:
65984                 createDocument:function(namespaceURI,  qualifiedName, doctype){// raises:INVALID_CHARACTER_ERR,NAMESPACE_ERR,WRONG_DOCUMENT_ERR
65985                         var doc = new Document();
65986                         doc.implementation = this;
65987                         doc.childNodes = new NodeList();
65988                         doc.doctype = doctype;
65989                         if(doctype){
65990                                 doc.appendChild(doctype);
65991                         }
65992                         if(qualifiedName){
65993                                 var root = doc.createElementNS(namespaceURI,qualifiedName);
65994                                 doc.appendChild(root);
65995                         }
65996                         return doc;
65997                 },
65998                 // Introduced in DOM Level 2:
65999                 createDocumentType:function(qualifiedName, publicId, systemId){// raises:INVALID_CHARACTER_ERR,NAMESPACE_ERR
66000                         var node = new DocumentType();
66001                         node.name = qualifiedName;
66002                         node.nodeName = qualifiedName;
66003                         node.publicId = publicId;
66004                         node.systemId = systemId;
66005                         // Introduced in DOM Level 2:
66006                         //readonly attribute DOMString        internalSubset;
66007                         
66008                         //TODO:..
66009                         //  readonly attribute NamedNodeMap     entities;
66010                         //  readonly attribute NamedNodeMap     notations;
66011                         return node;
66012                 }
66013         };
66014
66015
66016         /**
66017          * @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-1950641247
66018          */
66019
66020         function Node() {
66021         }
66022         Node.prototype = {
66023                 firstChild : null,
66024                 lastChild : null,
66025                 previousSibling : null,
66026                 nextSibling : null,
66027                 attributes : null,
66028                 parentNode : null,
66029                 childNodes : null,
66030                 ownerDocument : null,
66031                 nodeValue : null,
66032                 namespaceURI : null,
66033                 prefix : null,
66034                 localName : null,
66035                 // Modified in DOM Level 2:
66036                 insertBefore:function(newChild, refChild){//raises 
66037                         return _insertBefore(this,newChild,refChild);
66038                 },
66039                 replaceChild:function(newChild, oldChild){//raises 
66040                         this.insertBefore(newChild,oldChild);
66041                         if(oldChild){
66042                                 this.removeChild(oldChild);
66043                         }
66044                 },
66045                 removeChild:function(oldChild){
66046                         return _removeChild(this,oldChild);
66047                 },
66048                 appendChild:function(newChild){
66049                         return this.insertBefore(newChild,null);
66050                 },
66051                 hasChildNodes:function(){
66052                         return this.firstChild != null;
66053                 },
66054                 cloneNode:function(deep){
66055                         return cloneNode(this.ownerDocument||this,this,deep);
66056                 },
66057                 // Modified in DOM Level 2:
66058                 normalize:function(){
66059                         var child = this.firstChild;
66060                         while(child){
66061                                 var next = child.nextSibling;
66062                                 if(next && next.nodeType == TEXT_NODE && child.nodeType == TEXT_NODE){
66063                                         this.removeChild(next);
66064                                         child.appendData(next.data);
66065                                 }else {
66066                                         child.normalize();
66067                                         child = next;
66068                                 }
66069                         }
66070                 },
66071                 // Introduced in DOM Level 2:
66072                 isSupported:function(feature, version){
66073                         return this.ownerDocument.implementation.hasFeature(feature,version);
66074                 },
66075             // Introduced in DOM Level 2:
66076             hasAttributes:function(){
66077                 return this.attributes.length>0;
66078             },
66079             lookupPrefix:function(namespaceURI){
66080                 var el = this;
66081                 while(el){
66082                         var map = el._nsMap;
66083                         //console.dir(map)
66084                         if(map){
66085                                 for(var n in map){
66086                                         if(map[n] == namespaceURI){
66087                                                 return n;
66088                                         }
66089                                 }
66090                         }
66091                         el = el.nodeType == ATTRIBUTE_NODE?el.ownerDocument : el.parentNode;
66092                 }
66093                 return null;
66094             },
66095             // Introduced in DOM Level 3:
66096             lookupNamespaceURI:function(prefix){
66097                 var el = this;
66098                 while(el){
66099                         var map = el._nsMap;
66100                         //console.dir(map)
66101                         if(map){
66102                                 if(prefix in map){
66103                                         return map[prefix] ;
66104                                 }
66105                         }
66106                         el = el.nodeType == ATTRIBUTE_NODE?el.ownerDocument : el.parentNode;
66107                 }
66108                 return null;
66109             },
66110             // Introduced in DOM Level 3:
66111             isDefaultNamespace:function(namespaceURI){
66112                 var prefix = this.lookupPrefix(namespaceURI);
66113                 return prefix == null;
66114             }
66115         };
66116
66117
66118         function _xmlEncoder(c){
66119                 return c == '<' && '&lt;' ||
66120                  c == '>' && '&gt;' ||
66121                  c == '&' && '&amp;' ||
66122                  c == '"' && '&quot;' ||
66123                  '&#'+c.charCodeAt()+';'
66124         }
66125
66126
66127         copy$2(NodeType,Node);
66128         copy$2(NodeType,Node.prototype);
66129
66130         /**
66131          * @param callback return true for continue,false for break
66132          * @return boolean true: break visit;
66133          */
66134         function _visitNode(node,callback){
66135                 if(callback(node)){
66136                         return true;
66137                 }
66138                 if(node = node.firstChild){
66139                         do{
66140                                 if(_visitNode(node,callback)){return true}
66141                 }while(node=node.nextSibling)
66142             }
66143         }
66144
66145
66146
66147         function Document(){
66148         }
66149         function _onAddAttribute(doc,el,newAttr){
66150                 doc && doc._inc++;
66151                 var ns = newAttr.namespaceURI ;
66152                 if(ns == 'http://www.w3.org/2000/xmlns/'){
66153                         //update namespace
66154                         el._nsMap[newAttr.prefix?newAttr.localName:''] = newAttr.value;
66155                 }
66156         }
66157         function _onRemoveAttribute(doc,el,newAttr,remove){
66158                 doc && doc._inc++;
66159                 var ns = newAttr.namespaceURI ;
66160                 if(ns == 'http://www.w3.org/2000/xmlns/'){
66161                         //update namespace
66162                         delete el._nsMap[newAttr.prefix?newAttr.localName:''];
66163                 }
66164         }
66165         function _onUpdateChild(doc,el,newChild){
66166                 if(doc && doc._inc){
66167                         doc._inc++;
66168                         //update childNodes
66169                         var cs = el.childNodes;
66170                         if(newChild){
66171                                 cs[cs.length++] = newChild;
66172                         }else {
66173                                 //console.log(1)
66174                                 var child = el.firstChild;
66175                                 var i = 0;
66176                                 while(child){
66177                                         cs[i++] = child;
66178                                         child =child.nextSibling;
66179                                 }
66180                                 cs.length = i;
66181                         }
66182                 }
66183         }
66184
66185         /**
66186          * attributes;
66187          * children;
66188          * 
66189          * writeable properties:
66190          * nodeValue,Attr:value,CharacterData:data
66191          * prefix
66192          */
66193         function _removeChild(parentNode,child){
66194                 var previous = child.previousSibling;
66195                 var next = child.nextSibling;
66196                 if(previous){
66197                         previous.nextSibling = next;
66198                 }else {
66199                         parentNode.firstChild = next;
66200                 }
66201                 if(next){
66202                         next.previousSibling = previous;
66203                 }else {
66204                         parentNode.lastChild = previous;
66205                 }
66206                 _onUpdateChild(parentNode.ownerDocument,parentNode);
66207                 return child;
66208         }
66209         /**
66210          * preformance key(refChild == null)
66211          */
66212         function _insertBefore(parentNode,newChild,nextChild){
66213                 var cp = newChild.parentNode;
66214                 if(cp){
66215                         cp.removeChild(newChild);//remove and update
66216                 }
66217                 if(newChild.nodeType === DOCUMENT_FRAGMENT_NODE){
66218                         var newFirst = newChild.firstChild;
66219                         if (newFirst == null) {
66220                                 return newChild;
66221                         }
66222                         var newLast = newChild.lastChild;
66223                 }else {
66224                         newFirst = newLast = newChild;
66225                 }
66226                 var pre = nextChild ? nextChild.previousSibling : parentNode.lastChild;
66227
66228                 newFirst.previousSibling = pre;
66229                 newLast.nextSibling = nextChild;
66230                 
66231                 
66232                 if(pre){
66233                         pre.nextSibling = newFirst;
66234                 }else {
66235                         parentNode.firstChild = newFirst;
66236                 }
66237                 if(nextChild == null){
66238                         parentNode.lastChild = newLast;
66239                 }else {
66240                         nextChild.previousSibling = newLast;
66241                 }
66242                 do{
66243                         newFirst.parentNode = parentNode;
66244                 }while(newFirst !== newLast && (newFirst= newFirst.nextSibling))
66245                 _onUpdateChild(parentNode.ownerDocument||parentNode,parentNode);
66246                 //console.log(parentNode.lastChild.nextSibling == null)
66247                 if (newChild.nodeType == DOCUMENT_FRAGMENT_NODE) {
66248                         newChild.firstChild = newChild.lastChild = null;
66249                 }
66250                 return newChild;
66251         }
66252         function _appendSingleChild(parentNode,newChild){
66253                 var cp = newChild.parentNode;
66254                 if(cp){
66255                         var pre = parentNode.lastChild;
66256                         cp.removeChild(newChild);//remove and update
66257                         var pre = parentNode.lastChild;
66258                 }
66259                 var pre = parentNode.lastChild;
66260                 newChild.parentNode = parentNode;
66261                 newChild.previousSibling = pre;
66262                 newChild.nextSibling = null;
66263                 if(pre){
66264                         pre.nextSibling = newChild;
66265                 }else {
66266                         parentNode.firstChild = newChild;
66267                 }
66268                 parentNode.lastChild = newChild;
66269                 _onUpdateChild(parentNode.ownerDocument,parentNode,newChild);
66270                 return newChild;
66271                 //console.log("__aa",parentNode.lastChild.nextSibling == null)
66272         }
66273         Document.prototype = {
66274                 //implementation : null,
66275                 nodeName :  '#document',
66276                 nodeType :  DOCUMENT_NODE,
66277                 doctype :  null,
66278                 documentElement :  null,
66279                 _inc : 1,
66280                 
66281                 insertBefore :  function(newChild, refChild){//raises 
66282                         if(newChild.nodeType == DOCUMENT_FRAGMENT_NODE){
66283                                 var child = newChild.firstChild;
66284                                 while(child){
66285                                         var next = child.nextSibling;
66286                                         this.insertBefore(child,refChild);
66287                                         child = next;
66288                                 }
66289                                 return newChild;
66290                         }
66291                         if(this.documentElement == null && newChild.nodeType == ELEMENT_NODE){
66292                                 this.documentElement = newChild;
66293                         }
66294                         
66295                         return _insertBefore(this,newChild,refChild),(newChild.ownerDocument = this),newChild;
66296                 },
66297                 removeChild :  function(oldChild){
66298                         if(this.documentElement == oldChild){
66299                                 this.documentElement = null;
66300                         }
66301                         return _removeChild(this,oldChild);
66302                 },
66303                 // Introduced in DOM Level 2:
66304                 importNode : function(importedNode,deep){
66305                         return importNode(this,importedNode,deep);
66306                 },
66307                 // Introduced in DOM Level 2:
66308                 getElementById :        function(id){
66309                         var rtv = null;
66310                         _visitNode(this.documentElement,function(node){
66311                                 if(node.nodeType == ELEMENT_NODE){
66312                                         if(node.getAttribute('id') == id){
66313                                                 rtv = node;
66314                                                 return true;
66315                                         }
66316                                 }
66317                         });
66318                         return rtv;
66319                 },
66320                 
66321                 //document factory method:
66322                 createElement : function(tagName){
66323                         var node = new Element();
66324                         node.ownerDocument = this;
66325                         node.nodeName = tagName;
66326                         node.tagName = tagName;
66327                         node.childNodes = new NodeList();
66328                         var attrs       = node.attributes = new NamedNodeMap();
66329                         attrs._ownerElement = node;
66330                         return node;
66331                 },
66332                 createDocumentFragment :        function(){
66333                         var node = new DocumentFragment();
66334                         node.ownerDocument = this;
66335                         node.childNodes = new NodeList();
66336                         return node;
66337                 },
66338                 createTextNode :        function(data){
66339                         var node = new Text();
66340                         node.ownerDocument = this;
66341                         node.appendData(data);
66342                         return node;
66343                 },
66344                 createComment : function(data){
66345                         var node = new Comment();
66346                         node.ownerDocument = this;
66347                         node.appendData(data);
66348                         return node;
66349                 },
66350                 createCDATASection :    function(data){
66351                         var node = new CDATASection();
66352                         node.ownerDocument = this;
66353                         node.appendData(data);
66354                         return node;
66355                 },
66356                 createProcessingInstruction :   function(target,data){
66357                         var node = new ProcessingInstruction();
66358                         node.ownerDocument = this;
66359                         node.tagName = node.target = target;
66360                         node.nodeValue= node.data = data;
66361                         return node;
66362                 },
66363                 createAttribute :       function(name){
66364                         var node = new Attr();
66365                         node.ownerDocument      = this;
66366                         node.name = name;
66367                         node.nodeName   = name;
66368                         node.localName = name;
66369                         node.specified = true;
66370                         return node;
66371                 },
66372                 createEntityReference : function(name){
66373                         var node = new EntityReference();
66374                         node.ownerDocument      = this;
66375                         node.nodeName   = name;
66376                         return node;
66377                 },
66378                 // Introduced in DOM Level 2:
66379                 createElementNS :       function(namespaceURI,qualifiedName){
66380                         var node = new Element();
66381                         var pl = qualifiedName.split(':');
66382                         var attrs       = node.attributes = new NamedNodeMap();
66383                         node.childNodes = new NodeList();
66384                         node.ownerDocument = this;
66385                         node.nodeName = qualifiedName;
66386                         node.tagName = qualifiedName;
66387                         node.namespaceURI = namespaceURI;
66388                         if(pl.length == 2){
66389                                 node.prefix = pl[0];
66390                                 node.localName = pl[1];
66391                         }else {
66392                                 //el.prefix = null;
66393                                 node.localName = qualifiedName;
66394                         }
66395                         attrs._ownerElement = node;
66396                         return node;
66397                 },
66398                 // Introduced in DOM Level 2:
66399                 createAttributeNS :     function(namespaceURI,qualifiedName){
66400                         var node = new Attr();
66401                         var pl = qualifiedName.split(':');
66402                         node.ownerDocument = this;
66403                         node.nodeName = qualifiedName;
66404                         node.name = qualifiedName;
66405                         node.namespaceURI = namespaceURI;
66406                         node.specified = true;
66407                         if(pl.length == 2){
66408                                 node.prefix = pl[0];
66409                                 node.localName = pl[1];
66410                         }else {
66411                                 //el.prefix = null;
66412                                 node.localName = qualifiedName;
66413                         }
66414                         return node;
66415                 }
66416         };
66417         _extends(Document,Node);
66418
66419
66420         function Element() {
66421                 this._nsMap = {};
66422         }Element.prototype = {
66423                 nodeType : ELEMENT_NODE,
66424                 hasAttribute : function(name){
66425                         return this.getAttributeNode(name)!=null;
66426                 },
66427                 getAttribute : function(name){
66428                         var attr = this.getAttributeNode(name);
66429                         return attr && attr.value || '';
66430                 },
66431                 getAttributeNode : function(name){
66432                         return this.attributes.getNamedItem(name);
66433                 },
66434                 setAttribute : function(name, value){
66435                         var attr = this.ownerDocument.createAttribute(name);
66436                         attr.value = attr.nodeValue = "" + value;
66437                         this.setAttributeNode(attr);
66438                 },
66439                 removeAttribute : function(name){
66440                         var attr = this.getAttributeNode(name);
66441                         attr && this.removeAttributeNode(attr);
66442                 },
66443                 
66444                 //four real opeartion method
66445                 appendChild:function(newChild){
66446                         if(newChild.nodeType === DOCUMENT_FRAGMENT_NODE){
66447                                 return this.insertBefore(newChild,null);
66448                         }else {
66449                                 return _appendSingleChild(this,newChild);
66450                         }
66451                 },
66452                 setAttributeNode : function(newAttr){
66453                         return this.attributes.setNamedItem(newAttr);
66454                 },
66455                 setAttributeNodeNS : function(newAttr){
66456                         return this.attributes.setNamedItemNS(newAttr);
66457                 },
66458                 removeAttributeNode : function(oldAttr){
66459                         //console.log(this == oldAttr.ownerElement)
66460                         return this.attributes.removeNamedItem(oldAttr.nodeName);
66461                 },
66462                 //get real attribute name,and remove it by removeAttributeNode
66463                 removeAttributeNS : function(namespaceURI, localName){
66464                         var old = this.getAttributeNodeNS(namespaceURI, localName);
66465                         old && this.removeAttributeNode(old);
66466                 },
66467                 
66468                 hasAttributeNS : function(namespaceURI, localName){
66469                         return this.getAttributeNodeNS(namespaceURI, localName)!=null;
66470                 },
66471                 getAttributeNS : function(namespaceURI, localName){
66472                         var attr = this.getAttributeNodeNS(namespaceURI, localName);
66473                         return attr && attr.value || '';
66474                 },
66475                 setAttributeNS : function(namespaceURI, qualifiedName, value){
66476                         var attr = this.ownerDocument.createAttributeNS(namespaceURI, qualifiedName);
66477                         attr.value = attr.nodeValue = "" + value;
66478                         this.setAttributeNode(attr);
66479                 },
66480                 getAttributeNodeNS : function(namespaceURI, localName){
66481                         return this.attributes.getNamedItemNS(namespaceURI, localName);
66482                 },
66483                 
66484                 getElementsByTagName : function(tagName){
66485                         return new LiveNodeList(this,function(base){
66486                                 var ls = [];
66487                                 _visitNode(base,function(node){
66488                                         if(node !== base && node.nodeType == ELEMENT_NODE && (tagName === '*' || node.tagName == tagName)){
66489                                                 ls.push(node);
66490                                         }
66491                                 });
66492                                 return ls;
66493                         });
66494                 },
66495                 getElementsByTagNameNS : function(namespaceURI, localName){
66496                         return new LiveNodeList(this,function(base){
66497                                 var ls = [];
66498                                 _visitNode(base,function(node){
66499                                         if(node !== base && node.nodeType === ELEMENT_NODE && (namespaceURI === '*' || node.namespaceURI === namespaceURI) && (localName === '*' || node.localName == localName)){
66500                                                 ls.push(node);
66501                                         }
66502                                 });
66503                                 return ls;
66504                                 
66505                         });
66506                 }
66507         };
66508         Document.prototype.getElementsByTagName = Element.prototype.getElementsByTagName;
66509         Document.prototype.getElementsByTagNameNS = Element.prototype.getElementsByTagNameNS;
66510
66511
66512         _extends(Element,Node);
66513         function Attr() {
66514         }Attr.prototype.nodeType = ATTRIBUTE_NODE;
66515         _extends(Attr,Node);
66516
66517
66518         function CharacterData() {
66519         }CharacterData.prototype = {
66520                 data : '',
66521                 substringData : function(offset, count) {
66522                         return this.data.substring(offset, offset+count);
66523                 },
66524                 appendData: function(text) {
66525                         text = this.data+text;
66526                         this.nodeValue = this.data = text;
66527                         this.length = text.length;
66528                 },
66529                 insertData: function(offset,text) {
66530                         this.replaceData(offset,0,text);
66531                 
66532                 },
66533                 appendChild:function(newChild){
66534                         throw new Error(ExceptionMessage[HIERARCHY_REQUEST_ERR])
66535                 },
66536                 deleteData: function(offset, count) {
66537                         this.replaceData(offset,count,"");
66538                 },
66539                 replaceData: function(offset, count, text) {
66540                         var start = this.data.substring(0,offset);
66541                         var end = this.data.substring(offset+count);
66542                         text = start + text + end;
66543                         this.nodeValue = this.data = text;
66544                         this.length = text.length;
66545                 }
66546         };
66547         _extends(CharacterData,Node);
66548         function Text() {
66549         }Text.prototype = {
66550                 nodeName : "#text",
66551                 nodeType : TEXT_NODE,
66552                 splitText : function(offset) {
66553                         var text = this.data;
66554                         var newText = text.substring(offset);
66555                         text = text.substring(0, offset);
66556                         this.data = this.nodeValue = text;
66557                         this.length = text.length;
66558                         var newNode = this.ownerDocument.createTextNode(newText);
66559                         if(this.parentNode){
66560                                 this.parentNode.insertBefore(newNode, this.nextSibling);
66561                         }
66562                         return newNode;
66563                 }
66564         };
66565         _extends(Text,CharacterData);
66566         function Comment() {
66567         }Comment.prototype = {
66568                 nodeName : "#comment",
66569                 nodeType : COMMENT_NODE
66570         };
66571         _extends(Comment,CharacterData);
66572
66573         function CDATASection() {
66574         }CDATASection.prototype = {
66575                 nodeName : "#cdata-section",
66576                 nodeType : CDATA_SECTION_NODE
66577         };
66578         _extends(CDATASection,CharacterData);
66579
66580
66581         function DocumentType() {
66582         }DocumentType.prototype.nodeType = DOCUMENT_TYPE_NODE;
66583         _extends(DocumentType,Node);
66584
66585         function Notation() {
66586         }Notation.prototype.nodeType = NOTATION_NODE;
66587         _extends(Notation,Node);
66588
66589         function Entity() {
66590         }Entity.prototype.nodeType = ENTITY_NODE;
66591         _extends(Entity,Node);
66592
66593         function EntityReference() {
66594         }EntityReference.prototype.nodeType = ENTITY_REFERENCE_NODE;
66595         _extends(EntityReference,Node);
66596
66597         function DocumentFragment() {
66598         }DocumentFragment.prototype.nodeName =  "#document-fragment";
66599         DocumentFragment.prototype.nodeType =   DOCUMENT_FRAGMENT_NODE;
66600         _extends(DocumentFragment,Node);
66601
66602
66603         function ProcessingInstruction() {
66604         }
66605         ProcessingInstruction.prototype.nodeType = PROCESSING_INSTRUCTION_NODE;
66606         _extends(ProcessingInstruction,Node);
66607         function XMLSerializer$1(){}
66608         XMLSerializer$1.prototype.serializeToString = function(node,isHtml,nodeFilter){
66609                 return nodeSerializeToString.call(node,isHtml,nodeFilter);
66610         };
66611         Node.prototype.toString = nodeSerializeToString;
66612         function nodeSerializeToString(isHtml,nodeFilter){
66613                 var buf = [];
66614                 var refNode = this.nodeType == 9?this.documentElement:this;
66615                 var prefix = refNode.prefix;
66616                 var uri = refNode.namespaceURI;
66617                 
66618                 if(uri && prefix == null){
66619                         //console.log(prefix)
66620                         var prefix = refNode.lookupPrefix(uri);
66621                         if(prefix == null){
66622                                 //isHTML = true;
66623                                 var visibleNamespaces=[
66624                                 {namespace:uri,prefix:null} ];
66625                         }
66626                 }
66627                 serializeToString(this,buf,isHtml,nodeFilter,visibleNamespaces);
66628                 //console.log('###',this.nodeType,uri,prefix,buf.join(''))
66629                 return buf.join('');
66630         }
66631         function needNamespaceDefine(node,isHTML, visibleNamespaces) {
66632                 var prefix = node.prefix||'';
66633                 var uri = node.namespaceURI;
66634                 if (!prefix && !uri){
66635                         return false;
66636                 }
66637                 if (prefix === "xml" && uri === "http://www.w3.org/XML/1998/namespace" 
66638                         || uri == 'http://www.w3.org/2000/xmlns/'){
66639                         return false;
66640                 }
66641                 
66642                 var i = visibleNamespaces.length; 
66643                 //console.log('@@@@',node.tagName,prefix,uri,visibleNamespaces)
66644                 while (i--) {
66645                         var ns = visibleNamespaces[i];
66646                         // get namespace prefix
66647                         //console.log(node.nodeType,node.tagName,ns.prefix,prefix)
66648                         if (ns.prefix == prefix){
66649                                 return ns.namespace != uri;
66650                         }
66651                 }
66652                 //console.log(isHTML,uri,prefix=='')
66653                 //if(isHTML && prefix ==null && uri == 'http://www.w3.org/1999/xhtml'){
66654                 //      return false;
66655                 //}
66656                 //node.flag = '11111'
66657                 //console.error(3,true,node.flag,node.prefix,node.namespaceURI)
66658                 return true;
66659         }
66660         function serializeToString(node,buf,isHTML,nodeFilter,visibleNamespaces){
66661                 if(nodeFilter){
66662                         node = nodeFilter(node);
66663                         if(node){
66664                                 if(typeof node == 'string'){
66665                                         buf.push(node);
66666                                         return;
66667                                 }
66668                         }else {
66669                                 return;
66670                         }
66671                         //buf.sort.apply(attrs, attributeSorter);
66672                 }
66673                 switch(node.nodeType){
66674                 case ELEMENT_NODE:
66675                         if (!visibleNamespaces) { visibleNamespaces = []; }
66676                         var startVisibleNamespaces = visibleNamespaces.length;
66677                         var attrs = node.attributes;
66678                         var len = attrs.length;
66679                         var child = node.firstChild;
66680                         var nodeName = node.tagName;
66681                         
66682                         isHTML =  (htmlns === node.namespaceURI) ||isHTML; 
66683                         buf.push('<',nodeName);
66684                         
66685                         
66686                         
66687                         for(var i=0;i<len;i++){
66688                                 // add namespaces for attributes
66689                                 var attr = attrs.item(i);
66690                                 if (attr.prefix == 'xmlns') {
66691                                         visibleNamespaces.push({ prefix: attr.localName, namespace: attr.value });
66692                                 }else if(attr.nodeName == 'xmlns'){
66693                                         visibleNamespaces.push({ prefix: '', namespace: attr.value });
66694                                 }
66695                         }
66696                         for(var i=0;i<len;i++){
66697                                 var attr = attrs.item(i);
66698                                 if (needNamespaceDefine(attr,isHTML, visibleNamespaces)) {
66699                                         var prefix = attr.prefix||'';
66700                                         var uri = attr.namespaceURI;
66701                                         var ns = prefix ? ' xmlns:' + prefix : " xmlns";
66702                                         buf.push(ns, '="' , uri , '"');
66703                                         visibleNamespaces.push({ prefix: prefix, namespace:uri });
66704                                 }
66705                                 serializeToString(attr,buf,isHTML,nodeFilter,visibleNamespaces);
66706                         }
66707                         // add namespace for current node               
66708                         if (needNamespaceDefine(node,isHTML, visibleNamespaces)) {
66709                                 var prefix = node.prefix||'';
66710                                 var uri = node.namespaceURI;
66711                                 var ns = prefix ? ' xmlns:' + prefix : " xmlns";
66712                                 buf.push(ns, '="' , uri , '"');
66713                                 visibleNamespaces.push({ prefix: prefix, namespace:uri });
66714                         }
66715                         
66716                         if(child || isHTML && !/^(?:meta|link|img|br|hr|input)$/i.test(nodeName)){
66717                                 buf.push('>');
66718                                 //if is cdata child node
66719                                 if(isHTML && /^script$/i.test(nodeName)){
66720                                         while(child){
66721                                                 if(child.data){
66722                                                         buf.push(child.data);
66723                                                 }else {
66724                                                         serializeToString(child,buf,isHTML,nodeFilter,visibleNamespaces);
66725                                                 }
66726                                                 child = child.nextSibling;
66727                                         }
66728                                 }else
66729                                 {
66730                                         while(child){
66731                                                 serializeToString(child,buf,isHTML,nodeFilter,visibleNamespaces);
66732                                                 child = child.nextSibling;
66733                                         }
66734                                 }
66735                                 buf.push('</',nodeName,'>');
66736                         }else {
66737                                 buf.push('/>');
66738                         }
66739                         // remove added visible namespaces
66740                         //visibleNamespaces.length = startVisibleNamespaces;
66741                         return;
66742                 case DOCUMENT_NODE:
66743                 case DOCUMENT_FRAGMENT_NODE:
66744                         var child = node.firstChild;
66745                         while(child){
66746                                 serializeToString(child,buf,isHTML,nodeFilter,visibleNamespaces);
66747                                 child = child.nextSibling;
66748                         }
66749                         return;
66750                 case ATTRIBUTE_NODE:
66751                         return buf.push(' ',node.name,'="',node.value.replace(/[<&"]/g,_xmlEncoder),'"');
66752                 case TEXT_NODE:
66753                         return buf.push(node.data.replace(/[<&]/g,_xmlEncoder));
66754                 case CDATA_SECTION_NODE:
66755                         return buf.push( '<![CDATA[',node.data,']]>');
66756                 case COMMENT_NODE:
66757                         return buf.push( "<!--",node.data,"-->");
66758                 case DOCUMENT_TYPE_NODE:
66759                         var pubid = node.publicId;
66760                         var sysid = node.systemId;
66761                         buf.push('<!DOCTYPE ',node.name);
66762                         if(pubid){
66763                                 buf.push(' PUBLIC "',pubid);
66764                                 if (sysid && sysid!='.') {
66765                                         buf.push( '" "',sysid);
66766                                 }
66767                                 buf.push('">');
66768                         }else if(sysid && sysid!='.'){
66769                                 buf.push(' SYSTEM "',sysid,'">');
66770                         }else {
66771                                 var sub = node.internalSubset;
66772                                 if(sub){
66773                                         buf.push(" [",sub,"]");
66774                                 }
66775                                 buf.push(">");
66776                         }
66777                         return;
66778                 case PROCESSING_INSTRUCTION_NODE:
66779                         return buf.push( "<?",node.target," ",node.data,"?>");
66780                 case ENTITY_REFERENCE_NODE:
66781                         return buf.push( '&',node.nodeName,';');
66782                 //case ENTITY_NODE:
66783                 //case NOTATION_NODE:
66784                 default:
66785                         buf.push('??',node.nodeName);
66786                 }
66787         }
66788         function importNode(doc,node,deep){
66789                 var node2;
66790                 switch (node.nodeType) {
66791                 case ELEMENT_NODE:
66792                         node2 = node.cloneNode(false);
66793                         node2.ownerDocument = doc;
66794                         //var attrs = node2.attributes;
66795                         //var len = attrs.length;
66796                         //for(var i=0;i<len;i++){
66797                                 //node2.setAttributeNodeNS(importNode(doc,attrs.item(i),deep));
66798                         //}
66799                 case DOCUMENT_FRAGMENT_NODE:
66800                         break;
66801                 case ATTRIBUTE_NODE:
66802                         deep = true;
66803                         break;
66804                 //case ENTITY_REFERENCE_NODE:
66805                 //case PROCESSING_INSTRUCTION_NODE:
66806                 ////case TEXT_NODE:
66807                 //case CDATA_SECTION_NODE:
66808                 //case COMMENT_NODE:
66809                 //      deep = false;
66810                 //      break;
66811                 //case DOCUMENT_NODE:
66812                 //case DOCUMENT_TYPE_NODE:
66813                 //cannot be imported.
66814                 //case ENTITY_NODE:
66815                 //case NOTATION_NODE:
66816                 //can not hit in level3
66817                 //default:throw e;
66818                 }
66819                 if(!node2){
66820                         node2 = node.cloneNode(false);//false
66821                 }
66822                 node2.ownerDocument = doc;
66823                 node2.parentNode = null;
66824                 if(deep){
66825                         var child = node.firstChild;
66826                         while(child){
66827                                 node2.appendChild(importNode(doc,child,deep));
66828                                 child = child.nextSibling;
66829                         }
66830                 }
66831                 return node2;
66832         }
66833         //
66834         //var _relationMap = {firstChild:1,lastChild:1,previousSibling:1,nextSibling:1,
66835         //                                      attributes:1,childNodes:1,parentNode:1,documentElement:1,doctype,};
66836         function cloneNode(doc,node,deep){
66837                 var node2 = new node.constructor();
66838                 for(var n in node){
66839                         var v = node[n];
66840                         if(typeof v != 'object' ){
66841                                 if(v != node2[n]){
66842                                         node2[n] = v;
66843                                 }
66844                         }
66845                 }
66846                 if(node.childNodes){
66847                         node2.childNodes = new NodeList();
66848                 }
66849                 node2.ownerDocument = doc;
66850                 switch (node2.nodeType) {
66851                 case ELEMENT_NODE:
66852                         var attrs       = node.attributes;
66853                         var attrs2      = node2.attributes = new NamedNodeMap();
66854                         var len = attrs.length;
66855                         attrs2._ownerElement = node2;
66856                         for(var i=0;i<len;i++){
66857                                 node2.setAttributeNode(cloneNode(doc,attrs.item(i),true));
66858                         }
66859                         break;  case ATTRIBUTE_NODE:
66860                         deep = true;
66861                 }
66862                 if(deep){
66863                         var child = node.firstChild;
66864                         while(child){
66865                                 node2.appendChild(cloneNode(doc,child,deep));
66866                                 child = child.nextSibling;
66867                         }
66868                 }
66869                 return node2;
66870         }
66871
66872         function __set__(object,key,value){
66873                 object[key] = value;
66874         }
66875         //do dynamic
66876         try{
66877                 if(Object.defineProperty){
66878                         Object.defineProperty(LiveNodeList.prototype,'length',{
66879                                 get:function(){
66880                                         _updateLiveList(this);
66881                                         return this.$$length;
66882                                 }
66883                         });
66884                         Object.defineProperty(Node.prototype,'textContent',{
66885                                 get:function(){
66886                                         return getTextContent(this);
66887                                 },
66888                                 set:function(data){
66889                                         switch(this.nodeType){
66890                                         case ELEMENT_NODE:
66891                                         case DOCUMENT_FRAGMENT_NODE:
66892                                                 while(this.firstChild){
66893                                                         this.removeChild(this.firstChild);
66894                                                 }
66895                                                 if(data || String(data)){
66896                                                         this.appendChild(this.ownerDocument.createTextNode(data));
66897                                                 }
66898                                                 break;
66899                                         default:
66900                                                 //TODO:
66901                                                 this.data = data;
66902                                                 this.value = data;
66903                                                 this.nodeValue = data;
66904                                         }
66905                                 }
66906                         });
66907                         
66908                         function getTextContent(node){
66909                                 switch(node.nodeType){
66910                                 case ELEMENT_NODE:
66911                                 case DOCUMENT_FRAGMENT_NODE:
66912                                         var buf = [];
66913                                         node = node.firstChild;
66914                                         while(node){
66915                                                 if(node.nodeType!==7 && node.nodeType !==8){
66916                                                         buf.push(getTextContent(node));
66917                                                 }
66918                                                 node = node.nextSibling;
66919                                         }
66920                                         return buf.join('');
66921                                 default:
66922                                         return node.nodeValue;
66923                                 }
66924                         }
66925                         __set__ = function(object,key,value){
66926                                 //console.log(value)
66927                                 object['$$'+key] = value;
66928                         };
66929                 }
66930         }catch(e){//ie8
66931         }
66932
66933         //if(typeof require == 'function'){
66934                 var DOMImplementation_1 = DOMImplementation;
66935                 var XMLSerializer_1 = XMLSerializer$1;
66936         //}
66937
66938         var dom = {
66939                 DOMImplementation: DOMImplementation_1,
66940                 XMLSerializer: XMLSerializer_1
66941         };
66942
66943         var domParser = createCommonjsModule(function (module, exports) {
66944         function DOMParser(options){
66945                 this.options = options ||{locator:{}};
66946                 
66947         }
66948         DOMParser.prototype.parseFromString = function(source,mimeType){
66949                 var options = this.options;
66950                 var sax =  new XMLReader();
66951                 var domBuilder = options.domBuilder || new DOMHandler();//contentHandler and LexicalHandler
66952                 var errorHandler = options.errorHandler;
66953                 var locator = options.locator;
66954                 var defaultNSMap = options.xmlns||{};
66955                 var entityMap = {'lt':'<','gt':'>','amp':'&','quot':'"','apos':"'"};
66956                 if(locator){
66957                         domBuilder.setDocumentLocator(locator);
66958                 }
66959                 
66960                 sax.errorHandler = buildErrorHandler(errorHandler,domBuilder,locator);
66961                 sax.domBuilder = options.domBuilder || domBuilder;
66962                 if(/\/x?html?$/.test(mimeType)){
66963                         entityMap.nbsp = '\xa0';
66964                         entityMap.copy = '\xa9';
66965                         defaultNSMap['']= 'http://www.w3.org/1999/xhtml';
66966                 }
66967                 defaultNSMap.xml = defaultNSMap.xml || 'http://www.w3.org/XML/1998/namespace';
66968                 if(source){
66969                         sax.parse(source,defaultNSMap,entityMap);
66970                 }else {
66971                         sax.errorHandler.error("invalid doc source");
66972                 }
66973                 return domBuilder.doc;
66974         };
66975         function buildErrorHandler(errorImpl,domBuilder,locator){
66976                 if(!errorImpl){
66977                         if(domBuilder instanceof DOMHandler){
66978                                 return domBuilder;
66979                         }
66980                         errorImpl = domBuilder ;
66981                 }
66982                 var errorHandler = {};
66983                 var isCallback = errorImpl instanceof Function;
66984                 locator = locator||{};
66985                 function build(key){
66986                         var fn = errorImpl[key];
66987                         if(!fn && isCallback){
66988                                 fn = errorImpl.length == 2?function(msg){errorImpl(key,msg);}:errorImpl;
66989                         }
66990                         errorHandler[key] = fn && function(msg){
66991                                 fn('[xmldom '+key+']\t'+msg+_locator(locator));
66992                         }||function(){};
66993                 }
66994                 build('warning');
66995                 build('error');
66996                 build('fatalError');
66997                 return errorHandler;
66998         }
66999
67000         //console.log('#\n\n\n\n\n\n\n####')
67001         /**
67002          * +ContentHandler+ErrorHandler
67003          * +LexicalHandler+EntityResolver2
67004          * -DeclHandler-DTDHandler 
67005          * 
67006          * DefaultHandler:EntityResolver, DTDHandler, ContentHandler, ErrorHandler
67007          * DefaultHandler2:DefaultHandler,LexicalHandler, DeclHandler, EntityResolver2
67008          * @link http://www.saxproject.org/apidoc/org/xml/sax/helpers/DefaultHandler.html
67009          */
67010         function DOMHandler() {
67011             this.cdata = false;
67012         }
67013         function position(locator,node){
67014                 node.lineNumber = locator.lineNumber;
67015                 node.columnNumber = locator.columnNumber;
67016         }
67017         /**
67018          * @see org.xml.sax.ContentHandler#startDocument
67019          * @link http://www.saxproject.org/apidoc/org/xml/sax/ContentHandler.html
67020          */ 
67021         DOMHandler.prototype = {
67022                 startDocument : function() {
67023                 this.doc = new DOMImplementation().createDocument(null, null, null);
67024                 if (this.locator) {
67025                         this.doc.documentURI = this.locator.systemId;
67026                 }
67027                 },
67028                 startElement:function(namespaceURI, localName, qName, attrs) {
67029                         var doc = this.doc;
67030                     var el = doc.createElementNS(namespaceURI, qName||localName);
67031                     var len = attrs.length;
67032                     appendElement(this, el);
67033                     this.currentElement = el;
67034                     
67035                         this.locator && position(this.locator,el);
67036                     for (var i = 0 ; i < len; i++) {
67037                         var namespaceURI = attrs.getURI(i);
67038                         var value = attrs.getValue(i);
67039                         var qName = attrs.getQName(i);
67040                                 var attr = doc.createAttributeNS(namespaceURI, qName);
67041                                 this.locator &&position(attrs.getLocator(i),attr);
67042                                 attr.value = attr.nodeValue = value;
67043                                 el.setAttributeNode(attr);
67044                     }
67045                 },
67046                 endElement:function(namespaceURI, localName, qName) {
67047                         var current = this.currentElement;
67048                         var tagName = current.tagName;
67049                         this.currentElement = current.parentNode;
67050                 },
67051                 startPrefixMapping:function(prefix, uri) {
67052                 },
67053                 endPrefixMapping:function(prefix) {
67054                 },
67055                 processingInstruction:function(target, data) {
67056                     var ins = this.doc.createProcessingInstruction(target, data);
67057                     this.locator && position(this.locator,ins);
67058                     appendElement(this, ins);
67059                 },
67060                 ignorableWhitespace:function(ch, start, length) {
67061                 },
67062                 characters:function(chars, start, length) {
67063                         chars = _toString.apply(this,arguments);
67064                         //console.log(chars)
67065                         if(chars){
67066                                 if (this.cdata) {
67067                                         var charNode = this.doc.createCDATASection(chars);
67068                                 } else {
67069                                         var charNode = this.doc.createTextNode(chars);
67070                                 }
67071                                 if(this.currentElement){
67072                                         this.currentElement.appendChild(charNode);
67073                                 }else if(/^\s*$/.test(chars)){
67074                                         this.doc.appendChild(charNode);
67075                                         //process xml
67076                                 }
67077                                 this.locator && position(this.locator,charNode);
67078                         }
67079                 },
67080                 skippedEntity:function(name) {
67081                 },
67082                 endDocument:function() {
67083                         this.doc.normalize();
67084                 },
67085                 setDocumentLocator:function (locator) {
67086                     if(this.locator = locator){// && !('lineNumber' in locator)){
67087                         locator.lineNumber = 0;
67088                     }
67089                 },
67090                 //LexicalHandler
67091                 comment:function(chars, start, length) {
67092                         chars = _toString.apply(this,arguments);
67093                     var comm = this.doc.createComment(chars);
67094                     this.locator && position(this.locator,comm);
67095                     appendElement(this, comm);
67096                 },
67097                 
67098                 startCDATA:function() {
67099                     //used in characters() methods
67100                     this.cdata = true;
67101                 },
67102                 endCDATA:function() {
67103                     this.cdata = false;
67104                 },
67105                 
67106                 startDTD:function(name, publicId, systemId) {
67107                         var impl = this.doc.implementation;
67108                     if (impl && impl.createDocumentType) {
67109                         var dt = impl.createDocumentType(name, publicId, systemId);
67110                         this.locator && position(this.locator,dt);
67111                         appendElement(this, dt);
67112                     }
67113                 },
67114                 /**
67115                  * @see org.xml.sax.ErrorHandler
67116                  * @link http://www.saxproject.org/apidoc/org/xml/sax/ErrorHandler.html
67117                  */
67118                 warning:function(error) {
67119                         console.warn('[xmldom warning]\t'+error,_locator(this.locator));
67120                 },
67121                 error:function(error) {
67122                         console.error('[xmldom error]\t'+error,_locator(this.locator));
67123                 },
67124                 fatalError:function(error) {
67125                         console.error('[xmldom fatalError]\t'+error,_locator(this.locator));
67126                     throw error;
67127                 }
67128         };
67129         function _locator(l){
67130                 if(l){
67131                         return '\n@'+(l.systemId ||'')+'#[line:'+l.lineNumber+',col:'+l.columnNumber+']'
67132                 }
67133         }
67134         function _toString(chars,start,length){
67135                 if(typeof chars == 'string'){
67136                         return chars.substr(start,length)
67137                 }else {//java sax connect width xmldom on rhino(what about: "? && !(chars instanceof String)")
67138                         if(chars.length >= start+length || start){
67139                                 return new java.lang.String(chars,start,length)+'';
67140                         }
67141                         return chars;
67142                 }
67143         }
67144
67145         /*
67146          * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/LexicalHandler.html
67147          * used method of org.xml.sax.ext.LexicalHandler:
67148          *  #comment(chars, start, length)
67149          *  #startCDATA()
67150          *  #endCDATA()
67151          *  #startDTD(name, publicId, systemId)
67152          *
67153          *
67154          * IGNORED method of org.xml.sax.ext.LexicalHandler:
67155          *  #endDTD()
67156          *  #startEntity(name)
67157          *  #endEntity(name)
67158          *
67159          *
67160          * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/DeclHandler.html
67161          * IGNORED method of org.xml.sax.ext.DeclHandler
67162          *      #attributeDecl(eName, aName, type, mode, value)
67163          *  #elementDecl(name, model)
67164          *  #externalEntityDecl(name, publicId, systemId)
67165          *  #internalEntityDecl(name, value)
67166          * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/EntityResolver2.html
67167          * IGNORED method of org.xml.sax.EntityResolver2
67168          *  #resolveEntity(String name,String publicId,String baseURI,String systemId)
67169          *  #resolveEntity(publicId, systemId)
67170          *  #getExternalSubset(name, baseURI)
67171          * @link http://www.saxproject.org/apidoc/org/xml/sax/DTDHandler.html
67172          * IGNORED method of org.xml.sax.DTDHandler
67173          *  #notationDecl(name, publicId, systemId) {};
67174          *  #unparsedEntityDecl(name, publicId, systemId, notationName) {};
67175          */
67176         "endDTD,startEntity,endEntity,attributeDecl,elementDecl,externalEntityDecl,internalEntityDecl,resolveEntity,getExternalSubset,notationDecl,unparsedEntityDecl".replace(/\w+/g,function(key){
67177                 DOMHandler.prototype[key] = function(){return null};
67178         });
67179
67180         /* 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 */
67181         function appendElement (hander,node) {
67182             if (!hander.currentElement) {
67183                 hander.doc.appendChild(node);
67184             } else {
67185                 hander.currentElement.appendChild(node);
67186             }
67187         }//appendChild and setAttributeNS are preformance key
67188
67189         //if(typeof require == 'function'){
67190                 var XMLReader = sax.XMLReader;
67191                 var DOMImplementation = exports.DOMImplementation = dom.DOMImplementation;
67192                 exports.XMLSerializer = dom.XMLSerializer ;
67193                 exports.DOMParser = DOMParser;
67194         //}
67195         });
67196
67197         var togeojson = createCommonjsModule(function (module, exports) {
67198         var toGeoJSON = (function() {
67199
67200             var removeSpace = /\s*/g,
67201                 trimSpace = /^\s*|\s*$/g,
67202                 splitSpace = /\s+/;
67203             // generate a short, numeric hash of a string
67204             function okhash(x) {
67205                 if (!x || !x.length) { return 0; }
67206                 for (var i = 0, h = 0; i < x.length; i++) {
67207                     h = ((h << 5) - h) + x.charCodeAt(i) | 0;
67208                 } return h;
67209             }
67210             // all Y children of X
67211             function get(x, y) { return x.getElementsByTagName(y); }
67212             function attr(x, y) { return x.getAttribute(y); }
67213             function attrf(x, y) { return parseFloat(attr(x, y)); }
67214             // one Y child of X, if any, otherwise null
67215             function get1(x, y) { var n = get(x, y); return n.length ? n[0] : null; }
67216             // https://developer.mozilla.org/en-US/docs/Web/API/Node.normalize
67217             function norm(el) { if (el.normalize) { el.normalize(); } return el; }
67218             // cast array x into numbers
67219             function numarray(x) {
67220                 for (var j = 0, o = []; j < x.length; j++) { o[j] = parseFloat(x[j]); }
67221                 return o;
67222             }
67223             // get the content of a text node, if any
67224             function nodeVal(x) {
67225                 if (x) { norm(x); }
67226                 return (x && x.textContent) || '';
67227             }
67228             // get the contents of multiple text nodes, if present
67229             function getMulti(x, ys) {
67230                 var o = {}, n, k;
67231                 for (k = 0; k < ys.length; k++) {
67232                     n = get1(x, ys[k]);
67233                     if (n) { o[ys[k]] = nodeVal(n); }
67234                 }
67235                 return o;
67236             }
67237             // add properties of Y to X, overwriting if present in both
67238             function extend(x, y) { for (var k in y) { x[k] = y[k]; } }
67239             // get one coordinate from a coordinate array, if any
67240             function coord1(v) { return numarray(v.replace(removeSpace, '').split(',')); }
67241             // get all coordinates from a coordinate array as [[],[]]
67242             function coord(v) {
67243                 var coords = v.replace(trimSpace, '').split(splitSpace),
67244                     o = [];
67245                 for (var i = 0; i < coords.length; i++) {
67246                     o.push(coord1(coords[i]));
67247                 }
67248                 return o;
67249             }
67250             function coordPair(x) {
67251                 var ll = [attrf(x, 'lon'), attrf(x, 'lat')],
67252                     ele = get1(x, 'ele'),
67253                     // handle namespaced attribute in browser
67254                     heartRate = get1(x, 'gpxtpx:hr') || get1(x, 'hr'),
67255                     time = get1(x, 'time'),
67256                     e;
67257                 if (ele) {
67258                     e = parseFloat(nodeVal(ele));
67259                     if (!isNaN(e)) {
67260                         ll.push(e);
67261                     }
67262                 }
67263                 return {
67264                     coordinates: ll,
67265                     time: time ? nodeVal(time) : null,
67266                     heartRate: heartRate ? parseFloat(nodeVal(heartRate)) : null
67267                 };
67268             }
67269
67270             // create a new feature collection parent object
67271             function fc() {
67272                 return {
67273                     type: 'FeatureCollection',
67274                     features: []
67275                 };
67276             }
67277
67278             var serializer;
67279             if (typeof XMLSerializer !== 'undefined') {
67280                 /* istanbul ignore next */
67281                 serializer = new XMLSerializer();
67282             // only require xmldom in a node environment
67283             } else if ( typeof process === 'object' && !process.browser) {
67284                 serializer = new (domParser.XMLSerializer)();
67285             }
67286             function xml2str(str) {
67287                 // IE9 will create a new XMLSerializer but it'll crash immediately.
67288                 // This line is ignored because we don't run coverage tests in IE9
67289                 /* istanbul ignore next */
67290                 if (str.xml !== undefined) { return str.xml; }
67291                 return serializer.serializeToString(str);
67292             }
67293
67294             var t = {
67295                 kml: function(doc) {
67296
67297                     var gj = fc(),
67298                         // styleindex keeps track of hashed styles in order to match features
67299                         styleIndex = {}, styleByHash = {},
67300                         // stylemapindex keeps track of style maps to expose in properties
67301                         styleMapIndex = {},
67302                         // atomic geospatial types supported by KML - MultiGeometry is
67303                         // handled separately
67304                         geotypes = ['Polygon', 'LineString', 'Point', 'Track', 'gx:Track'],
67305                         // all root placemarks in the file
67306                         placemarks = get(doc, 'Placemark'),
67307                         styles = get(doc, 'Style'),
67308                         styleMaps = get(doc, 'StyleMap');
67309
67310                     for (var k = 0; k < styles.length; k++) {
67311                         var hash = okhash(xml2str(styles[k])).toString(16);
67312                         styleIndex['#' + attr(styles[k], 'id')] = hash;
67313                         styleByHash[hash] = styles[k];
67314                     }
67315                     for (var l = 0; l < styleMaps.length; l++) {
67316                         styleIndex['#' + attr(styleMaps[l], 'id')] = okhash(xml2str(styleMaps[l])).toString(16);
67317                         var pairs = get(styleMaps[l], 'Pair');
67318                         var pairsMap = {};
67319                         for (var m = 0; m < pairs.length; m++) {
67320                             pairsMap[nodeVal(get1(pairs[m], 'key'))] = nodeVal(get1(pairs[m], 'styleUrl'));
67321                         }
67322                         styleMapIndex['#' + attr(styleMaps[l], 'id')] = pairsMap;
67323
67324                     }
67325                     for (var j = 0; j < placemarks.length; j++) {
67326                         gj.features = gj.features.concat(getPlacemark(placemarks[j]));
67327                     }
67328                     function kmlColor(v) {
67329                         var color, opacity;
67330                         v = v || '';
67331                         if (v.substr(0, 1) === '#') { v = v.substr(1); }
67332                         if (v.length === 6 || v.length === 3) { color = v; }
67333                         if (v.length === 8) {
67334                             opacity = parseInt(v.substr(0, 2), 16) / 255;
67335                             color = '#' + v.substr(6, 2) +
67336                                 v.substr(4, 2) +
67337                                 v.substr(2, 2);
67338                         }
67339                         return [color, isNaN(opacity) ? undefined : opacity];
67340                     }
67341                     function gxCoord(v) { return numarray(v.split(' ')); }
67342                     function gxCoords(root) {
67343                         var elems = get(root, 'coord'), coords = [], times = [];
67344                         if (elems.length === 0) { elems = get(root, 'gx:coord'); }
67345                         for (var i = 0; i < elems.length; i++) { coords.push(gxCoord(nodeVal(elems[i]))); }
67346                         var timeElems = get(root, 'when');
67347                         for (var j = 0; j < timeElems.length; j++) { times.push(nodeVal(timeElems[j])); }
67348                         return {
67349                             coords: coords,
67350                             times: times
67351                         };
67352                     }
67353                     function getGeometry(root) {
67354                         var geomNode, geomNodes, i, j, k, geoms = [], coordTimes = [];
67355                         if (get1(root, 'MultiGeometry')) { return getGeometry(get1(root, 'MultiGeometry')); }
67356                         if (get1(root, 'MultiTrack')) { return getGeometry(get1(root, 'MultiTrack')); }
67357                         if (get1(root, 'gx:MultiTrack')) { return getGeometry(get1(root, 'gx:MultiTrack')); }
67358                         for (i = 0; i < geotypes.length; i++) {
67359                             geomNodes = get(root, geotypes[i]);
67360                             if (geomNodes) {
67361                                 for (j = 0; j < geomNodes.length; j++) {
67362                                     geomNode = geomNodes[j];
67363                                     if (geotypes[i] === 'Point') {
67364                                         geoms.push({
67365                                             type: 'Point',
67366                                             coordinates: coord1(nodeVal(get1(geomNode, 'coordinates')))
67367                                         });
67368                                     } else if (geotypes[i] === 'LineString') {
67369                                         geoms.push({
67370                                             type: 'LineString',
67371                                             coordinates: coord(nodeVal(get1(geomNode, 'coordinates')))
67372                                         });
67373                                     } else if (geotypes[i] === 'Polygon') {
67374                                         var rings = get(geomNode, 'LinearRing'),
67375                                             coords = [];
67376                                         for (k = 0; k < rings.length; k++) {
67377                                             coords.push(coord(nodeVal(get1(rings[k], 'coordinates'))));
67378                                         }
67379                                         geoms.push({
67380                                             type: 'Polygon',
67381                                             coordinates: coords
67382                                         });
67383                                     } else if (geotypes[i] === 'Track' ||
67384                                         geotypes[i] === 'gx:Track') {
67385                                         var track = gxCoords(geomNode);
67386                                         geoms.push({
67387                                             type: 'LineString',
67388                                             coordinates: track.coords
67389                                         });
67390                                         if (track.times.length) { coordTimes.push(track.times); }
67391                                     }
67392                                 }
67393                             }
67394                         }
67395                         return {
67396                             geoms: geoms,
67397                             coordTimes: coordTimes
67398                         };
67399                     }
67400                     function getPlacemark(root) {
67401                         var geomsAndTimes = getGeometry(root), i, properties = {},
67402                             name = nodeVal(get1(root, 'name')),
67403                             address = nodeVal(get1(root, 'address')),
67404                             styleUrl = nodeVal(get1(root, 'styleUrl')),
67405                             description = nodeVal(get1(root, 'description')),
67406                             timeSpan = get1(root, 'TimeSpan'),
67407                             timeStamp = get1(root, 'TimeStamp'),
67408                             extendedData = get1(root, 'ExtendedData'),
67409                             lineStyle = get1(root, 'LineStyle'),
67410                             polyStyle = get1(root, 'PolyStyle'),
67411                             visibility = get1(root, 'visibility');
67412
67413                         if (!geomsAndTimes.geoms.length) { return []; }
67414                         if (name) { properties.name = name; }
67415                         if (address) { properties.address = address; }
67416                         if (styleUrl) {
67417                             if (styleUrl[0] !== '#') {
67418                                 styleUrl = '#' + styleUrl;
67419                             }
67420
67421                             properties.styleUrl = styleUrl;
67422                             if (styleIndex[styleUrl]) {
67423                                 properties.styleHash = styleIndex[styleUrl];
67424                             }
67425                             if (styleMapIndex[styleUrl]) {
67426                                 properties.styleMapHash = styleMapIndex[styleUrl];
67427                                 properties.styleHash = styleIndex[styleMapIndex[styleUrl].normal];
67428                             }
67429                             // Try to populate the lineStyle or polyStyle since we got the style hash
67430                             var style = styleByHash[properties.styleHash];
67431                             if (style) {
67432                                 if (!lineStyle) { lineStyle = get1(style, 'LineStyle'); }
67433                                 if (!polyStyle) { polyStyle = get1(style, 'PolyStyle'); }
67434                             }
67435                         }
67436                         if (description) { properties.description = description; }
67437                         if (timeSpan) {
67438                             var begin = nodeVal(get1(timeSpan, 'begin'));
67439                             var end = nodeVal(get1(timeSpan, 'end'));
67440                             properties.timespan = { begin: begin, end: end };
67441                         }
67442                         if (timeStamp) {
67443                             properties.timestamp = nodeVal(get1(timeStamp, 'when'));
67444                         }
67445                         if (lineStyle) {
67446                             var linestyles = kmlColor(nodeVal(get1(lineStyle, 'color'))),
67447                                 color = linestyles[0],
67448                                 opacity = linestyles[1],
67449                                 width = parseFloat(nodeVal(get1(lineStyle, 'width')));
67450                             if (color) { properties.stroke = color; }
67451                             if (!isNaN(opacity)) { properties['stroke-opacity'] = opacity; }
67452                             if (!isNaN(width)) { properties['stroke-width'] = width; }
67453                         }
67454                         if (polyStyle) {
67455                             var polystyles = kmlColor(nodeVal(get1(polyStyle, 'color'))),
67456                                 pcolor = polystyles[0],
67457                                 popacity = polystyles[1],
67458                                 fill = nodeVal(get1(polyStyle, 'fill')),
67459                                 outline = nodeVal(get1(polyStyle, 'outline'));
67460                             if (pcolor) { properties.fill = pcolor; }
67461                             if (!isNaN(popacity)) { properties['fill-opacity'] = popacity; }
67462                             if (fill) { properties['fill-opacity'] = fill === '1' ? properties['fill-opacity'] || 1 : 0; }
67463                             if (outline) { properties['stroke-opacity'] = outline === '1' ? properties['stroke-opacity'] || 1 : 0; }
67464                         }
67465                         if (extendedData) {
67466                             var datas = get(extendedData, 'Data'),
67467                                 simpleDatas = get(extendedData, 'SimpleData');
67468
67469                             for (i = 0; i < datas.length; i++) {
67470                                 properties[datas[i].getAttribute('name')] = nodeVal(get1(datas[i], 'value'));
67471                             }
67472                             for (i = 0; i < simpleDatas.length; i++) {
67473                                 properties[simpleDatas[i].getAttribute('name')] = nodeVal(simpleDatas[i]);
67474                             }
67475                         }
67476                         if (visibility) {
67477                             properties.visibility = nodeVal(visibility);
67478                         }
67479                         if (geomsAndTimes.coordTimes.length) {
67480                             properties.coordTimes = (geomsAndTimes.coordTimes.length === 1) ?
67481                                 geomsAndTimes.coordTimes[0] : geomsAndTimes.coordTimes;
67482                         }
67483                         var feature = {
67484                             type: 'Feature',
67485                             geometry: (geomsAndTimes.geoms.length === 1) ? geomsAndTimes.geoms[0] : {
67486                                 type: 'GeometryCollection',
67487                                 geometries: geomsAndTimes.geoms
67488                             },
67489                             properties: properties
67490                         };
67491                         if (attr(root, 'id')) { feature.id = attr(root, 'id'); }
67492                         return [feature];
67493                     }
67494                     return gj;
67495                 },
67496                 gpx: function(doc) {
67497                     var i,
67498                         tracks = get(doc, 'trk'),
67499                         routes = get(doc, 'rte'),
67500                         waypoints = get(doc, 'wpt'),
67501                         // a feature collection
67502                         gj = fc(),
67503                         feature;
67504                     for (i = 0; i < tracks.length; i++) {
67505                         feature = getTrack(tracks[i]);
67506                         if (feature) { gj.features.push(feature); }
67507                     }
67508                     for (i = 0; i < routes.length; i++) {
67509                         feature = getRoute(routes[i]);
67510                         if (feature) { gj.features.push(feature); }
67511                     }
67512                     for (i = 0; i < waypoints.length; i++) {
67513                         gj.features.push(getPoint(waypoints[i]));
67514                     }
67515                     function getPoints(node, pointname) {
67516                         var pts = get(node, pointname),
67517                             line = [],
67518                             times = [],
67519                             heartRates = [],
67520                             l = pts.length;
67521                         if (l < 2) { return {}; }  // Invalid line in GeoJSON
67522                         for (var i = 0; i < l; i++) {
67523                             var c = coordPair(pts[i]);
67524                             line.push(c.coordinates);
67525                             if (c.time) { times.push(c.time); }
67526                             if (c.heartRate) { heartRates.push(c.heartRate); }
67527                         }
67528                         return {
67529                             line: line,
67530                             times: times,
67531                             heartRates: heartRates
67532                         };
67533                     }
67534                     function getTrack(node) {
67535                         var segments = get(node, 'trkseg'),
67536                             track = [],
67537                             times = [],
67538                             heartRates = [],
67539                             line;
67540                         for (var i = 0; i < segments.length; i++) {
67541                             line = getPoints(segments[i], 'trkpt');
67542                             if (line) {
67543                                 if (line.line) { track.push(line.line); }
67544                                 if (line.times && line.times.length) { times.push(line.times); }
67545                                 if (line.heartRates && line.heartRates.length) { heartRates.push(line.heartRates); }
67546                             }
67547                         }
67548                         if (track.length === 0) { return; }
67549                         var properties = getProperties(node);
67550                         extend(properties, getLineStyle(get1(node, 'extensions')));
67551                         if (times.length) { properties.coordTimes = track.length === 1 ? times[0] : times; }
67552                         if (heartRates.length) { properties.heartRates = track.length === 1 ? heartRates[0] : heartRates; }
67553                         return {
67554                             type: 'Feature',
67555                             properties: properties,
67556                             geometry: {
67557                                 type: track.length === 1 ? 'LineString' : 'MultiLineString',
67558                                 coordinates: track.length === 1 ? track[0] : track
67559                             }
67560                         };
67561                     }
67562                     function getRoute(node) {
67563                         var line = getPoints(node, 'rtept');
67564                         if (!line.line) { return; }
67565                         var prop = getProperties(node);
67566                         extend(prop, getLineStyle(get1(node, 'extensions')));
67567                         var routeObj = {
67568                             type: 'Feature',
67569                             properties: prop,
67570                             geometry: {
67571                                 type: 'LineString',
67572                                 coordinates: line.line
67573                             }
67574                         };
67575                         return routeObj;
67576                     }
67577                     function getPoint(node) {
67578                         var prop = getProperties(node);
67579                         extend(prop, getMulti(node, ['sym']));
67580                         return {
67581                             type: 'Feature',
67582                             properties: prop,
67583                             geometry: {
67584                                 type: 'Point',
67585                                 coordinates: coordPair(node).coordinates
67586                             }
67587                         };
67588                     }
67589                     function getLineStyle(extensions) {
67590                         var style = {};
67591                         if (extensions) {
67592                             var lineStyle = get1(extensions, 'line');
67593                             if (lineStyle) {
67594                                 var color = nodeVal(get1(lineStyle, 'color')),
67595                                     opacity = parseFloat(nodeVal(get1(lineStyle, 'opacity'))),
67596                                     width = parseFloat(nodeVal(get1(lineStyle, 'width')));
67597                                 if (color) { style.stroke = color; }
67598                                 if (!isNaN(opacity)) { style['stroke-opacity'] = opacity; }
67599                                 // GPX width is in mm, convert to px with 96 px per inch
67600                                 if (!isNaN(width)) { style['stroke-width'] = width * 96 / 25.4; }
67601                             }
67602                         }
67603                         return style;
67604                     }
67605                     function getProperties(node) {
67606                         var prop = getMulti(node, ['name', 'cmt', 'desc', 'type', 'time', 'keywords']),
67607                             links = get(node, 'link');
67608                         if (links.length) { prop.links = []; }
67609                         for (var i = 0, link; i < links.length; i++) {
67610                             link = { href: attr(links[i], 'href') };
67611                             extend(link, getMulti(links[i], ['text', 'type']));
67612                             prop.links.push(link);
67613                         }
67614                         return prop;
67615                     }
67616                     return gj;
67617                 }
67618             };
67619             return t;
67620         })();
67621
67622         { module.exports = toGeoJSON; }
67623         });
67624
67625         var _initialized = false;
67626         var _enabled = false;
67627         var _geojson;
67628
67629
67630         function svgData(projection, context, dispatch) {
67631             var throttledRedraw = throttle(function () { dispatch.call('change'); }, 1000);
67632             var _showLabels = true;
67633             var detected = utilDetect();
67634             var layer = select(null);
67635             var _vtService;
67636             var _fileList;
67637             var _template;
67638             var _src;
67639
67640
67641             function init() {
67642                 if (_initialized) { return; }  // run once
67643
67644                 _geojson = {};
67645                 _enabled = true;
67646
67647                 function over() {
67648                     event.stopPropagation();
67649                     event.preventDefault();
67650                     event.dataTransfer.dropEffect = 'copy';
67651                 }
67652
67653                 context.container()
67654                     .attr('dropzone', 'copy')
67655                     .on('drop.svgData', function() {
67656                         event.stopPropagation();
67657                         event.preventDefault();
67658                         if (!detected.filedrop) { return; }
67659                         drawData.fileList(event.dataTransfer.files);
67660                     })
67661                     .on('dragenter.svgData', over)
67662                     .on('dragexit.svgData', over)
67663                     .on('dragover.svgData', over);
67664
67665                 _initialized = true;
67666             }
67667
67668
67669             function getService() {
67670                 if (services.vectorTile && !_vtService) {
67671                     _vtService = services.vectorTile;
67672                     _vtService.event.on('loadedData', throttledRedraw);
67673                 } else if (!services.vectorTile && _vtService) {
67674                     _vtService = null;
67675                 }
67676
67677                 return _vtService;
67678             }
67679
67680
67681             function showLayer() {
67682                 layerOn();
67683
67684                 layer
67685                     .style('opacity', 0)
67686                     .transition()
67687                     .duration(250)
67688                     .style('opacity', 1)
67689                     .on('end', function () { dispatch.call('change'); });
67690             }
67691
67692
67693             function hideLayer() {
67694                 throttledRedraw.cancel();
67695
67696                 layer
67697                     .transition()
67698                     .duration(250)
67699                     .style('opacity', 0)
67700                     .on('end', layerOff);
67701             }
67702
67703
67704             function layerOn() {
67705                 layer.style('display', 'block');
67706             }
67707
67708
67709             function layerOff() {
67710                 layer.selectAll('.viewfield-group').remove();
67711                 layer.style('display', 'none');
67712             }
67713
67714
67715             // ensure that all geojson features in a collection have IDs
67716             function ensureIDs(gj) {
67717                 if (!gj) { return null; }
67718
67719                 if (gj.type === 'FeatureCollection') {
67720                     for (var i = 0; i < gj.features.length; i++) {
67721                         ensureFeatureID(gj.features[i]);
67722                     }
67723                 } else {
67724                     ensureFeatureID(gj);
67725                 }
67726                 return gj;
67727             }
67728
67729
67730             // ensure that each single Feature object has a unique ID
67731             function ensureFeatureID(feature) {
67732                 if (!feature) { return; }
67733                 feature.__featurehash__ = utilHashcode(fastJsonStableStringify(feature));
67734                 return feature;
67735             }
67736
67737
67738             // Prefer an array of Features instead of a FeatureCollection
67739             function getFeatures(gj) {
67740                 if (!gj) { return []; }
67741
67742                 if (gj.type === 'FeatureCollection') {
67743                     return gj.features;
67744                 } else {
67745                     return [gj];
67746                 }
67747             }
67748
67749
67750             function featureKey(d) {
67751                 return d.__featurehash__;
67752             }
67753
67754
67755             function isPolygon(d) {
67756                 return d.geometry.type === 'Polygon' || d.geometry.type === 'MultiPolygon';
67757             }
67758
67759
67760             function clipPathID(d) {
67761                 return 'ideditor-data-' + d.__featurehash__ + '-clippath';
67762             }
67763
67764
67765             function featureClasses(d) {
67766                 return [
67767                     'data' + d.__featurehash__,
67768                     d.geometry.type,
67769                     isPolygon(d) ? 'area' : '',
67770                     d.__layerID__ || ''
67771                 ].filter(Boolean).join(' ');
67772             }
67773
67774
67775             function drawData(selection) {
67776                 var vtService = getService();
67777                 var getPath = svgPath(projection).geojson;
67778                 var getAreaPath = svgPath(projection, null, true).geojson;
67779                 var hasData = drawData.hasData();
67780
67781                 layer = selection.selectAll('.layer-mapdata')
67782                     .data(_enabled && hasData ? [0] : []);
67783
67784                 layer.exit()
67785                     .remove();
67786
67787                 layer = layer.enter()
67788                     .append('g')
67789                     .attr('class', 'layer-mapdata')
67790                     .merge(layer);
67791
67792                 var surface = context.surface();
67793                 if (!surface || surface.empty()) { return; }  // not ready to draw yet, starting up
67794
67795
67796                 // Gather data
67797                 var geoData, polygonData;
67798                 if (_template && vtService) {   // fetch data from vector tile service
67799                     var sourceID = _template;
67800                     vtService.loadTiles(sourceID, _template, projection);
67801                     geoData = vtService.data(sourceID, projection);
67802                 } else {
67803                     geoData = getFeatures(_geojson);
67804                 }
67805                 geoData = geoData.filter(getPath);
67806                 polygonData = geoData.filter(isPolygon);
67807
67808
67809                 // Draw clip paths for polygons
67810                 var clipPaths = surface.selectAll('defs').selectAll('.clipPath-data')
67811                    .data(polygonData, featureKey);
67812
67813                 clipPaths.exit()
67814                    .remove();
67815
67816                 var clipPathsEnter = clipPaths.enter()
67817                    .append('clipPath')
67818                    .attr('class', 'clipPath-data')
67819                    .attr('id', clipPathID);
67820
67821                 clipPathsEnter
67822                    .append('path');
67823
67824                 clipPaths.merge(clipPathsEnter)
67825                    .selectAll('path')
67826                    .attr('d', getAreaPath);
67827
67828
67829                 // Draw fill, shadow, stroke layers
67830                 var datagroups = layer
67831                     .selectAll('g.datagroup')
67832                     .data(['fill', 'shadow', 'stroke']);
67833
67834                 datagroups = datagroups.enter()
67835                     .append('g')
67836                     .attr('class', function(d) { return 'datagroup datagroup-' + d; })
67837                     .merge(datagroups);
67838
67839
67840                 // Draw paths
67841                 var pathData = {
67842                     fill: polygonData,
67843                     shadow: geoData,
67844                     stroke: geoData
67845                 };
67846
67847                 var paths = datagroups
67848                     .selectAll('path')
67849                     .data(function(layer) { return pathData[layer]; }, featureKey);
67850
67851                 // exit
67852                 paths.exit()
67853                     .remove();
67854
67855                 // enter/update
67856                 paths = paths.enter()
67857                     .append('path')
67858                     .attr('class', function(d) {
67859                         var datagroup = this.parentNode.__data__;
67860                         return 'pathdata ' + datagroup + ' ' + featureClasses(d);
67861                     })
67862                     .attr('clip-path', function(d) {
67863                         var datagroup = this.parentNode.__data__;
67864                         return datagroup === 'fill' ? ('url(#' + clipPathID(d) + ')') : null;
67865                     })
67866                     .merge(paths)
67867                     .attr('d', function(d) {
67868                         var datagroup = this.parentNode.__data__;
67869                         return datagroup === 'fill' ? getAreaPath(d) : getPath(d);
67870                     });
67871
67872
67873                 // Draw labels
67874                 layer
67875                     .call(drawLabels, 'label-halo', geoData)
67876                     .call(drawLabels, 'label', geoData);
67877
67878
67879                 function drawLabels(selection, textClass, data) {
67880                     var labelPath = d3_geoPath(projection);
67881                     var labelData = data.filter(function(d) {
67882                         return _showLabels && d.properties && (d.properties.desc || d.properties.name);
67883                     });
67884
67885                     var labels = selection.selectAll('text.' + textClass)
67886                         .data(labelData, featureKey);
67887
67888                     // exit
67889                     labels.exit()
67890                         .remove();
67891
67892                     // enter/update
67893                     labels = labels.enter()
67894                         .append('text')
67895                         .attr('class', function(d) { return textClass + ' ' + featureClasses(d); })
67896                         .merge(labels)
67897                         .text(function(d) {
67898                             return d.properties.desc || d.properties.name;
67899                         })
67900                         .attr('x', function(d) {
67901                             var centroid = labelPath.centroid(d);
67902                             return centroid[0] + 11;
67903                         })
67904                         .attr('y', function(d) {
67905                             var centroid = labelPath.centroid(d);
67906                             return centroid[1];
67907                         });
67908                 }
67909             }
67910
67911
67912             function getExtension(fileName) {
67913                 if (!fileName) { return; }
67914
67915                 var re = /\.(gpx|kml|(geo)?json)$/i;
67916                 var match = fileName.toLowerCase().match(re);
67917                 return match && match.length && match[0];
67918             }
67919
67920
67921             function xmlToDom(textdata) {
67922                 return (new DOMParser()).parseFromString(textdata, 'text/xml');
67923             }
67924
67925
67926             drawData.setFile = function(extension, data) {
67927                 _template = null;
67928                 _fileList = null;
67929                 _geojson = null;
67930                 _src = null;
67931
67932                 var gj;
67933                 switch (extension) {
67934                     case '.gpx':
67935                         gj = togeojson.gpx(xmlToDom(data));
67936                         break;
67937                     case '.kml':
67938                         gj = togeojson.kml(xmlToDom(data));
67939                         break;
67940                     case '.geojson':
67941                     case '.json':
67942                         gj = JSON.parse(data);
67943                         break;
67944                 }
67945
67946                 gj = gj || {};
67947                 if (Object.keys(gj).length) {
67948                     _geojson = ensureIDs(gj);
67949                     _src = extension + ' data file';
67950                     this.fitZoom();
67951                 }
67952
67953                 dispatch.call('change');
67954                 return this;
67955             };
67956
67957
67958             drawData.showLabels = function(val) {
67959                 if (!arguments.length) { return _showLabels; }
67960
67961                 _showLabels = val;
67962                 return this;
67963             };
67964
67965
67966             drawData.enabled = function(val) {
67967                 if (!arguments.length) { return _enabled; }
67968
67969                 _enabled = val;
67970                 if (_enabled) {
67971                     showLayer();
67972                 } else {
67973                     hideLayer();
67974                 }
67975
67976                 dispatch.call('change');
67977                 return this;
67978             };
67979
67980
67981             drawData.hasData = function() {
67982                 var gj = _geojson || {};
67983                 return !!(_template || Object.keys(gj).length);
67984             };
67985
67986
67987             drawData.template = function(val, src) {
67988                 if (!arguments.length) { return _template; }
67989
67990                 // test source against OSM imagery blacklists..
67991                 var osm = context.connection();
67992                 if (osm) {
67993                     var blacklists = osm.imageryBlacklists();
67994                     var fail = false;
67995                     var tested = 0;
67996                     var regex;
67997
67998                     for (var i = 0; i < blacklists.length; i++) {
67999                         try {
68000                             regex = new RegExp(blacklists[i]);
68001                             fail = regex.test(val);
68002                             tested++;
68003                             if (fail) { break; }
68004                         } catch (e) {
68005                             /* noop */
68006                         }
68007                     }
68008
68009                     // ensure at least one test was run.
68010                     if (!tested) {
68011                         regex = new RegExp('.*\.google(apis)?\..*/(vt|kh)[\?/].*([xyz]=.*){3}.*');
68012                         fail = regex.test(val);
68013                     }
68014                 }
68015
68016                 _template = val;
68017                 _fileList = null;
68018                 _geojson = null;
68019
68020                 // strip off the querystring/hash from the template,
68021                 // it often includes the access token
68022                 _src = src || ('vectortile:' + val.split(/[?#]/)[0]);
68023
68024                 dispatch.call('change');
68025                 return this;
68026             };
68027
68028
68029             drawData.geojson = function(gj, src) {
68030                 if (!arguments.length) { return _geojson; }
68031
68032                 _template = null;
68033                 _fileList = null;
68034                 _geojson = null;
68035                 _src = null;
68036
68037                 gj = gj || {};
68038                 if (Object.keys(gj).length) {
68039                     _geojson = ensureIDs(gj);
68040                     _src = src || 'unknown.geojson';
68041                 }
68042
68043                 dispatch.call('change');
68044                 return this;
68045             };
68046
68047
68048             drawData.fileList = function(fileList) {
68049                 if (!arguments.length) { return _fileList; }
68050
68051                 _template = null;
68052                 _fileList = fileList;
68053                 _geojson = null;
68054                 _src = null;
68055
68056                 if (!fileList || !fileList.length) { return this; }
68057                 var f = fileList[0];
68058                 var extension = getExtension(f.name);
68059                 var reader = new FileReader();
68060                 reader.onload = (function() {
68061                     return function(e) {
68062                         drawData.setFile(extension, e.target.result);
68063                     };
68064                 })();
68065
68066                 reader.readAsText(f);
68067
68068                 return this;
68069             };
68070
68071
68072             drawData.url = function(url, defaultExtension) {
68073                 _template = null;
68074                 _fileList = null;
68075                 _geojson = null;
68076                 _src = null;
68077
68078                 // strip off any querystring/hash from the url before checking extension
68079                 var testUrl = url.split(/[?#]/)[0];
68080                 var extension = getExtension(testUrl) || defaultExtension;
68081                 if (extension) {
68082                     _template = null;
68083                     d3_text(url)
68084                         .then(function(data) {
68085                             drawData.setFile(extension, data);
68086                         })
68087                         .catch(function() {
68088                             /* ignore */
68089                         });
68090
68091                 } else {
68092                     drawData.template(url);
68093                 }
68094
68095                 return this;
68096             };
68097
68098
68099             drawData.getSrc = function() {
68100                 return _src || '';
68101             };
68102
68103
68104             drawData.fitZoom = function() {
68105                 var features = getFeatures(_geojson);
68106                 if (!features.length) { return; }
68107
68108                 var map = context.map();
68109                 var viewport = map.trimmedExtent().polygon();
68110                 var coords = features.reduce(function(coords, feature) {
68111                     var geom = feature.geometry;
68112                     if (!geom) { return coords; }
68113
68114                     var c = geom.coordinates;
68115
68116                     /* eslint-disable no-fallthrough */
68117                     switch (geom.type) {
68118                         case 'Point':
68119                             c = [c];
68120                         case 'MultiPoint':
68121                         case 'LineString':
68122                             break;
68123
68124                         case 'MultiPolygon':
68125                             c = utilArrayFlatten(c);
68126                         case 'Polygon':
68127                         case 'MultiLineString':
68128                             c = utilArrayFlatten(c);
68129                             break;
68130                     }
68131                     /* eslint-enable no-fallthrough */
68132
68133                     return utilArrayUnion(coords, c);
68134                 }, []);
68135
68136                 if (!geoPolygonIntersectsPolygon(viewport, coords, true)) {
68137                     var extent = geoExtent(d3_geoBounds({ type: 'LineString', coordinates: coords }));
68138                     map.centerZoom(extent.center(), map.trimmedExtentZoom(extent));
68139                 }
68140
68141                 return this;
68142             };
68143
68144
68145             init();
68146             return drawData;
68147         }
68148
68149         function svgDebug(projection, context) {
68150
68151           function drawDebug(selection) {
68152             var showTile = context.getDebug('tile');
68153             var showCollision = context.getDebug('collision');
68154             var showImagery = context.getDebug('imagery');
68155             var showTouchTargets = context.getDebug('target');
68156             var showDownloaded = context.getDebug('downloaded');
68157
68158             var debugData = [];
68159             if (showTile) {
68160               debugData.push({ class: 'red', label: 'tile' });
68161             }
68162             if (showCollision) {
68163               debugData.push({ class: 'yellow', label: 'collision' });
68164             }
68165             if (showImagery) {
68166               debugData.push({ class: 'orange', label: 'imagery' });
68167             }
68168             if (showTouchTargets) {
68169               debugData.push({ class: 'pink', label: 'touchTargets' });
68170             }
68171             if (showDownloaded) {
68172               debugData.push({ class: 'purple', label: 'downloaded' });
68173             }
68174
68175
68176             var legend = context.container().select('.main-content')
68177               .selectAll('.debug-legend')
68178               .data(debugData.length ? [0] : []);
68179
68180             legend.exit()
68181               .remove();
68182
68183             legend = legend.enter()
68184               .append('div')
68185               .attr('class', 'fillD debug-legend')
68186               .merge(legend);
68187
68188
68189             var legendItems = legend.selectAll('.debug-legend-item')
68190               .data(debugData, function (d) { return d.label; });
68191
68192             legendItems.exit()
68193               .remove();
68194
68195             legendItems.enter()
68196               .append('span')
68197               .attr('class', function (d) { return ("debug-legend-item " + (d.class)); })
68198               .text(function (d) { return d.label; });
68199
68200
68201             var layer = selection.selectAll('.layer-debug')
68202               .data(showImagery || showDownloaded ? [0] : []);
68203
68204             layer.exit()
68205               .remove();
68206
68207             layer = layer.enter()
68208               .append('g')
68209               .attr('class', 'layer-debug')
68210               .merge(layer);
68211
68212
68213             // imagery
68214             var extent = context.map().extent();
68215             _mainFileFetcher.get('imagery')
68216               .then(function (d) {
68217                 var hits = (showImagery && d.query.bbox(extent.rectangle(), true)) || [];
68218                 var features = hits.map(function (d) { return d.features[d.id]; });
68219
68220                 var imagery = layer.selectAll('path.debug-imagery')
68221                   .data(features);
68222
68223                 imagery.exit()
68224                   .remove();
68225
68226                 imagery.enter()
68227                   .append('path')
68228                   .attr('class', 'debug-imagery debug orange');
68229               })
68230               .catch(function () { /* ignore */ });
68231
68232             // downloaded
68233             var osm = context.connection();
68234             var dataDownloaded = [];
68235             if (osm && showDownloaded) {
68236               var rtree = osm.caches('get').tile.rtree;
68237               dataDownloaded = rtree.all().map(function (bbox) {
68238                 return {
68239                   type: 'Feature',
68240                   properties: { id: bbox.id },
68241                   geometry: {
68242                     type: 'Polygon',
68243                     coordinates: [[
68244                       [ bbox.minX, bbox.minY ],
68245                       [ bbox.minX, bbox.maxY ],
68246                       [ bbox.maxX, bbox.maxY ],
68247                       [ bbox.maxX, bbox.minY ],
68248                       [ bbox.minX, bbox.minY ]
68249                     ]]
68250                   }
68251                 };
68252               });
68253             }
68254
68255             var downloaded = layer
68256               .selectAll('path.debug-downloaded')
68257               .data(showDownloaded ? dataDownloaded : []);
68258
68259             downloaded.exit()
68260               .remove();
68261
68262             downloaded.enter()
68263               .append('path')
68264               .attr('class', 'debug-downloaded debug purple');
68265
68266             // update
68267             layer.selectAll('path')
68268               .attr('d', svgPath(projection).geojson);
68269           }
68270
68271
68272           // This looks strange because `enabled` methods on other layers are
68273           // chainable getter/setters, and this one is just a getter.
68274           drawDebug.enabled = function() {
68275             if (!arguments.length) {
68276               return context.getDebug('tile') ||
68277                 context.getDebug('collision') ||
68278                 context.getDebug('imagery') ||
68279                 context.getDebug('target') ||
68280                 context.getDebug('downloaded');
68281             } else {
68282                 return this;
68283             }
68284           };
68285
68286
68287           return drawDebug;
68288         }
68289
68290         var _layerEnabled = false;
68291         var _qaService;
68292
68293         function svgKeepRight(projection, context, dispatch) {
68294           var throttledRedraw = throttle(function () { return dispatch.call('change'); }, 1000);
68295           var minZoom = 12;
68296
68297           var touchLayer = select(null);
68298           var drawLayer = select(null);
68299           var layerVisible = false;
68300
68301           function markerPath(selection, klass) {
68302             selection
68303               .attr('class', klass)
68304               .attr('transform', 'translate(-4, -24)')
68305               .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');
68306           }
68307
68308           // Loosely-coupled keepRight service for fetching issues.
68309           function getService() {
68310             if (services.keepRight && !_qaService) {
68311               _qaService = services.keepRight;
68312               _qaService.on('loaded', throttledRedraw);
68313             } else if (!services.keepRight && _qaService) {
68314               _qaService = null;
68315             }
68316
68317             return _qaService;
68318           }
68319
68320           // Show the markers
68321           function editOn() {
68322             if (!layerVisible) {
68323               layerVisible = true;
68324               drawLayer
68325                 .style('display', 'block');
68326             }
68327           }
68328
68329           // Immediately remove the markers and their touch targets
68330           function editOff() {
68331             if (layerVisible) {
68332               layerVisible = false;
68333               drawLayer
68334                 .style('display', 'none');
68335               drawLayer.selectAll('.qaItem.keepRight')
68336                 .remove();
68337               touchLayer.selectAll('.qaItem.keepRight')
68338                 .remove();
68339             }
68340           }
68341
68342           // Enable the layer.  This shows the markers and transitions them to visible.
68343           function layerOn() {
68344             editOn();
68345
68346             drawLayer
68347               .style('opacity', 0)
68348               .transition()
68349               .duration(250)
68350               .style('opacity', 1)
68351               .on('end interrupt', function () { return dispatch.call('change'); });
68352           }
68353
68354           // Disable the layer.  This transitions the layer invisible and then hides the markers.
68355           function layerOff() {
68356             throttledRedraw.cancel();
68357             drawLayer.interrupt();
68358             touchLayer.selectAll('.qaItem.keepRight')
68359               .remove();
68360
68361             drawLayer
68362               .transition()
68363               .duration(250)
68364               .style('opacity', 0)
68365               .on('end interrupt', function () {
68366                 editOff();
68367                 dispatch.call('change');
68368               });
68369           }
68370
68371           // Update the issue markers
68372           function updateMarkers() {
68373             if (!layerVisible || !_layerEnabled) { return; }
68374
68375             var service = getService();
68376             var selectedID = context.selectedErrorID();
68377             var data = (service ? service.getItems(projection) : []);
68378             var getTransform = svgPointTransform(projection);
68379
68380             // Draw markers..
68381             var markers = drawLayer.selectAll('.qaItem.keepRight')
68382               .data(data, function (d) { return d.id; });
68383
68384             // exit
68385             markers.exit()
68386               .remove();
68387
68388             // enter
68389             var markersEnter = markers.enter()
68390               .append('g')
68391                 .attr('class', function (d) { return ("qaItem " + (d.service) + " itemId-" + (d.id) + " itemType-" + (d.parentIssueType)); });
68392
68393             markersEnter
68394               .append('ellipse')
68395                 .attr('cx', 0.5)
68396                 .attr('cy', 1)
68397                 .attr('rx', 6.5)
68398                 .attr('ry', 3)
68399                 .attr('class', 'stroke');
68400
68401             markersEnter
68402               .append('path')
68403                 .call(markerPath, 'shadow');
68404
68405             markersEnter
68406               .append('use')
68407                 .attr('class', 'qaItem-fill')
68408                 .attr('width', '20px')
68409                 .attr('height', '20px')
68410                 .attr('x', '-8px')
68411                 .attr('y', '-22px')
68412                 .attr('xlink:href', '#iD-icon-bolt');
68413
68414             // update
68415             markers
68416               .merge(markersEnter)
68417               .sort(sortY)
68418                 .classed('selected', function (d) { return d.id === selectedID; })
68419                 .attr('transform', getTransform);
68420
68421
68422             // Draw targets..
68423             if (touchLayer.empty()) { return; }
68424             var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
68425
68426             var targets = touchLayer.selectAll('.qaItem.keepRight')
68427               .data(data, function (d) { return d.id; });
68428
68429             // exit
68430             targets.exit()
68431               .remove();
68432
68433             // enter/update
68434             targets.enter()
68435               .append('rect')
68436                 .attr('width', '20px')
68437                 .attr('height', '20px')
68438                 .attr('x', '-8px')
68439                 .attr('y', '-22px')
68440               .merge(targets)
68441               .sort(sortY)
68442                 .attr('class', function (d) { return ("qaItem " + (d.service) + " target " + fillClass + " itemId-" + (d.id)); })
68443                 .attr('transform', getTransform);
68444
68445
68446             function sortY(a, b) {
68447               return (a.id === selectedID) ? 1
68448                 : (b.id === selectedID) ? -1
68449                 : (a.severity === 'error' && b.severity !== 'error') ? 1
68450                 : (b.severity === 'error' && a.severity !== 'error') ? -1
68451                 : b.loc[1] - a.loc[1];
68452             }
68453           }
68454
68455           // Draw the keepRight layer and schedule loading issues and updating markers.
68456           function drawKeepRight(selection) {
68457             var service = getService();
68458
68459             var surface = context.surface();
68460             if (surface && !surface.empty()) {
68461               touchLayer = surface.selectAll('.data-layer.touch .layer-touch.markers');
68462             }
68463
68464             drawLayer = selection.selectAll('.layer-keepRight')
68465               .data(service ? [0] : []);
68466
68467             drawLayer.exit()
68468               .remove();
68469
68470             drawLayer = drawLayer.enter()
68471               .append('g')
68472                 .attr('class', 'layer-keepRight')
68473                 .style('display', _layerEnabled ? 'block' : 'none')
68474               .merge(drawLayer);
68475
68476             if (_layerEnabled) {
68477               if (service && ~~context.map().zoom() >= minZoom) {
68478                 editOn();
68479                 service.loadIssues(projection);
68480                 updateMarkers();
68481               } else {
68482                 editOff();
68483               }
68484             }
68485           }
68486
68487           // Toggles the layer on and off
68488           drawKeepRight.enabled = function(val) {
68489             if (!arguments.length) { return _layerEnabled; }
68490
68491             _layerEnabled = val;
68492             if (_layerEnabled) {
68493               layerOn();
68494             } else {
68495               layerOff();
68496               if (context.selectedErrorID()) {
68497                 context.enter(modeBrowse(context));
68498               }
68499             }
68500
68501             dispatch.call('change');
68502             return this;
68503           };
68504
68505           drawKeepRight.supported = function () { return !!getService(); };
68506
68507           return drawKeepRight;
68508         }
68509
68510         function svgGeolocate(projection) {
68511             var layer = select(null);
68512             var _position;
68513
68514
68515             function init() {
68516                 if (svgGeolocate.initialized) { return; }  // run once
68517                 svgGeolocate.enabled = false;
68518                 svgGeolocate.initialized = true;
68519             }
68520
68521             function showLayer() {
68522                 layer.style('display', 'block');
68523             }
68524
68525
68526             function hideLayer() {
68527                 layer
68528                     .transition()
68529                     .duration(250)
68530                     .style('opacity', 0);
68531             }
68532
68533             function layerOn() {
68534                 layer
68535                     .style('opacity', 0)
68536                     .transition()
68537                     .duration(250)
68538                     .style('opacity', 1);
68539
68540             }
68541
68542             function layerOff() {
68543                 layer.style('display', 'none');
68544             }
68545
68546             function transform(d) {
68547                 return svgPointTransform(projection)(d);
68548             }
68549
68550             function accuracy(accuracy, loc) { // converts accuracy to pixels...
68551                 var degreesRadius = geoMetersToLat(accuracy),
68552                     tangentLoc = [loc[0], loc[1] + degreesRadius],
68553                     projectedTangent = projection(tangentLoc),
68554                     projectedLoc = projection([loc[0], loc[1]]);
68555
68556                 // southern most point will have higher pixel value...
68557                return Math.round(projectedLoc[1] - projectedTangent[1]).toString();
68558             }
68559
68560             function update() {
68561                 var geolocation = { loc: [_position.coords.longitude, _position.coords.latitude] };
68562
68563                 var groups = layer.selectAll('.geolocations').selectAll('.geolocation')
68564                     .data([geolocation]);
68565
68566                 groups.exit()
68567                     .remove();
68568
68569                 var pointsEnter = groups.enter()
68570                     .append('g')
68571                     .attr('class', 'geolocation');
68572
68573                 pointsEnter
68574                     .append('circle')
68575                     .attr('class', 'geolocate-radius')
68576                     .attr('dx', '0')
68577                     .attr('dy', '0')
68578                     .attr('fill', 'rgb(15,128,225)')
68579                     .attr('fill-opacity', '0.3')
68580                     .attr('r', '0');
68581
68582                 pointsEnter
68583                     .append('circle')
68584                     .attr('dx', '0')
68585                     .attr('dy', '0')
68586                     .attr('fill', 'rgb(15,128,225)')
68587                     .attr('stroke', 'white')
68588                     .attr('stroke-width', '1.5')
68589                     .attr('r', '6');
68590
68591                 groups.merge(pointsEnter)
68592                     .attr('transform', transform);
68593
68594                 layer.select('.geolocate-radius').attr('r', accuracy(_position.coords.accuracy, geolocation.loc));
68595             }
68596
68597             function drawLocation(selection) {
68598                 var enabled = svgGeolocate.enabled;
68599
68600                 layer = selection.selectAll('.layer-geolocate')
68601                     .data([0]);
68602
68603                 layer.exit()
68604                     .remove();
68605
68606                 var layerEnter = layer.enter()
68607                     .append('g')
68608                     .attr('class', 'layer-geolocate')
68609                     .style('display', enabled ? 'block' : 'none');
68610
68611                 layerEnter
68612                     .append('g')
68613                     .attr('class', 'geolocations');
68614
68615                 layer = layerEnter
68616                     .merge(layer);
68617
68618                 if (enabled) {
68619                     update();
68620                 } else {
68621                     layerOff();
68622                 }
68623             }
68624
68625             drawLocation.enabled = function (position, enabled) {
68626                 if (!arguments.length) { return svgGeolocate.enabled; }
68627                 _position = position;
68628                 svgGeolocate.enabled = enabled;
68629                 if (svgGeolocate.enabled) {
68630                     showLayer();
68631                     layerOn();
68632                 } else {
68633                     hideLayer();
68634                 }
68635                 return this;
68636             };
68637
68638             init();
68639             return drawLocation;
68640         }
68641
68642         function svgLabels(projection, context) {
68643             var path = d3_geoPath(projection);
68644             var detected = utilDetect();
68645             var baselineHack = (detected.ie ||
68646                 detected.browser.toLowerCase() === 'edge' ||
68647                 (detected.browser.toLowerCase() === 'firefox' && detected.version >= 70));
68648             var _rdrawn = new RBush();
68649             var _rskipped = new RBush();
68650             var _textWidthCache = {};
68651             var _entitybboxes = {};
68652
68653             // Listed from highest to lowest priority
68654             var labelStack = [
68655                 ['line', 'aeroway', '*', 12],
68656                 ['line', 'highway', 'motorway', 12],
68657                 ['line', 'highway', 'trunk', 12],
68658                 ['line', 'highway', 'primary', 12],
68659                 ['line', 'highway', 'secondary', 12],
68660                 ['line', 'highway', 'tertiary', 12],
68661                 ['line', 'highway', '*', 12],
68662                 ['line', 'railway', '*', 12],
68663                 ['line', 'waterway', '*', 12],
68664                 ['area', 'aeroway', '*', 12],
68665                 ['area', 'amenity', '*', 12],
68666                 ['area', 'building', '*', 12],
68667                 ['area', 'historic', '*', 12],
68668                 ['area', 'leisure', '*', 12],
68669                 ['area', 'man_made', '*', 12],
68670                 ['area', 'natural', '*', 12],
68671                 ['area', 'shop', '*', 12],
68672                 ['area', 'tourism', '*', 12],
68673                 ['area', 'camp_site', '*', 12],
68674                 ['point', 'aeroway', '*', 10],
68675                 ['point', 'amenity', '*', 10],
68676                 ['point', 'building', '*', 10],
68677                 ['point', 'historic', '*', 10],
68678                 ['point', 'leisure', '*', 10],
68679                 ['point', 'man_made', '*', 10],
68680                 ['point', 'natural', '*', 10],
68681                 ['point', 'shop', '*', 10],
68682                 ['point', 'tourism', '*', 10],
68683                 ['point', 'camp_site', '*', 10],
68684                 ['line', 'name', '*', 12],
68685                 ['area', 'name', '*', 12],
68686                 ['point', 'name', '*', 10]
68687             ];
68688
68689
68690             function shouldSkipIcon(preset) {
68691                 var noIcons = ['building', 'landuse', 'natural'];
68692                 return noIcons.some(function(s) {
68693                     return preset.id.indexOf(s) >= 0;
68694                 });
68695             }
68696
68697
68698             function get(array, prop) {
68699                 return function(d, i) { return array[i][prop]; };
68700             }
68701
68702
68703             function textWidth(text, size, elem) {
68704                 var c = _textWidthCache[size];
68705                 if (!c) { c = _textWidthCache[size] = {}; }
68706
68707                 if (c[text]) {
68708                     return c[text];
68709
68710                 } else if (elem) {
68711                     c[text] = elem.getComputedTextLength();
68712                     return c[text];
68713
68714                 } else {
68715                     var str = encodeURIComponent(text).match(/%[CDEFcdef]/g);
68716                     if (str === null) {
68717                         return size / 3 * 2 * text.length;
68718                     } else {
68719                         return size / 3 * (2 * text.length + str.length);
68720                     }
68721                 }
68722             }
68723
68724
68725             function drawLinePaths(selection, entities, filter, classes, labels) {
68726                 var paths = selection.selectAll('path')
68727                     .filter(filter)
68728                     .data(entities, osmEntity.key);
68729
68730                 // exit
68731                 paths.exit()
68732                     .remove();
68733
68734                 // enter/update
68735                 paths.enter()
68736                     .append('path')
68737                     .style('stroke-width', get(labels, 'font-size'))
68738                     .attr('id', function(d) { return 'ideditor-labelpath-' + d.id; })
68739                     .attr('class', classes)
68740                     .merge(paths)
68741                     .attr('d', get(labels, 'lineString'));
68742             }
68743
68744
68745             function drawLineLabels(selection, entities, filter, classes, labels) {
68746                 var texts = selection.selectAll('text.' + classes)
68747                     .filter(filter)
68748                     .data(entities, osmEntity.key);
68749
68750                 // exit
68751                 texts.exit()
68752                     .remove();
68753
68754                 // enter
68755                 texts.enter()
68756                     .append('text')
68757                     .attr('class', function(d, i) { return classes + ' ' + labels[i].classes + ' ' + d.id; })
68758                     .attr('dy', baselineHack ? '0.35em' : null)
68759                     .append('textPath')
68760                     .attr('class', 'textpath');
68761
68762                 // update
68763                 selection.selectAll('text.' + classes).selectAll('.textpath')
68764                     .filter(filter)
68765                     .data(entities, osmEntity.key)
68766                     .attr('startOffset', '50%')
68767                     .attr('xlink:href', function(d) { return '#ideditor-labelpath-' + d.id; })
68768                     .text(utilDisplayNameForPath);
68769             }
68770
68771
68772             function drawPointLabels(selection, entities, filter, classes, labels) {
68773                 var texts = selection.selectAll('text.' + classes)
68774                     .filter(filter)
68775                     .data(entities, osmEntity.key);
68776
68777                 // exit
68778                 texts.exit()
68779                     .remove();
68780
68781                 // enter/update
68782                 texts.enter()
68783                     .append('text')
68784                     .attr('class', function(d, i) {
68785                         return classes + ' ' + labels[i].classes + ' ' + d.id;
68786                     })
68787                     .merge(texts)
68788                     .attr('x', get(labels, 'x'))
68789                     .attr('y', get(labels, 'y'))
68790                     .style('text-anchor', get(labels, 'textAnchor'))
68791                     .text(utilDisplayName)
68792                     .each(function(d, i) {
68793                         textWidth(utilDisplayName(d), labels[i].height, this);
68794                     });
68795             }
68796
68797
68798             function drawAreaLabels(selection, entities, filter, classes, labels) {
68799                 entities = entities.filter(hasText);
68800                 labels = labels.filter(hasText);
68801                 drawPointLabels(selection, entities, filter, classes, labels);
68802
68803                 function hasText(d, i) {
68804                     return labels[i].hasOwnProperty('x') && labels[i].hasOwnProperty('y');
68805                 }
68806             }
68807
68808
68809             function drawAreaIcons(selection, entities, filter, classes, labels) {
68810                 var icons = selection.selectAll('use.' + classes)
68811                     .filter(filter)
68812                     .data(entities, osmEntity.key);
68813
68814                 // exit
68815                 icons.exit()
68816                     .remove();
68817
68818                 // enter/update
68819                 icons.enter()
68820                     .append('use')
68821                     .attr('class', 'icon ' + classes)
68822                     .attr('width', '17px')
68823                     .attr('height', '17px')
68824                     .merge(icons)
68825                     .attr('transform', get(labels, 'transform'))
68826                     .attr('xlink:href', function(d) {
68827                         var preset = _mainPresetIndex.match(d, context.graph());
68828                         var picon = preset && preset.icon;
68829
68830                         if (!picon) {
68831                             return '';
68832                         } else {
68833                             var isMaki = /^maki-/.test(picon);
68834                             return '#' + picon + (isMaki ? '-15' : '');
68835                         }
68836                     });
68837             }
68838
68839
68840             function drawCollisionBoxes(selection, rtree, which) {
68841                 var classes = 'debug ' + which + ' ' + (which === 'debug-skipped' ? 'orange' : 'yellow');
68842
68843                 var gj = [];
68844                 if (context.getDebug('collision')) {
68845                     gj = rtree.all().map(function(d) {
68846                         return { type: 'Polygon', coordinates: [[
68847                             [d.minX, d.minY],
68848                             [d.maxX, d.minY],
68849                             [d.maxX, d.maxY],
68850                             [d.minX, d.maxY],
68851                             [d.minX, d.minY]
68852                         ]]};
68853                     });
68854                 }
68855
68856                 var boxes = selection.selectAll('.' + which)
68857                     .data(gj);
68858
68859                 // exit
68860                 boxes.exit()
68861                     .remove();
68862
68863                 // enter/update
68864                 boxes.enter()
68865                     .append('path')
68866                     .attr('class', classes)
68867                     .merge(boxes)
68868                     .attr('d', d3_geoPath());
68869             }
68870
68871
68872             function drawLabels(selection, graph, entities, filter, dimensions, fullRedraw) {
68873                 var wireframe = context.surface().classed('fill-wireframe');
68874                 var zoom = geoScaleToZoom(projection.scale());
68875
68876                 var labelable = [];
68877                 var renderNodeAs = {};
68878                 var i, j, k, entity, geometry;
68879
68880                 for (i = 0; i < labelStack.length; i++) {
68881                     labelable.push([]);
68882                 }
68883
68884                 if (fullRedraw) {
68885                     _rdrawn.clear();
68886                     _rskipped.clear();
68887                     _entitybboxes = {};
68888
68889                 } else {
68890                     for (i = 0; i < entities.length; i++) {
68891                         entity = entities[i];
68892                         var toRemove = []
68893                             .concat(_entitybboxes[entity.id] || [])
68894                             .concat(_entitybboxes[entity.id + 'I'] || []);
68895
68896                         for (j = 0; j < toRemove.length; j++) {
68897                             _rdrawn.remove(toRemove[j]);
68898                             _rskipped.remove(toRemove[j]);
68899                         }
68900                     }
68901                 }
68902
68903                 // Loop through all the entities to do some preprocessing
68904                 for (i = 0; i < entities.length; i++) {
68905                     entity = entities[i];
68906                     geometry = entity.geometry(graph);
68907
68908                     // Insert collision boxes around interesting points/vertices
68909                     if (geometry === 'point' || (geometry === 'vertex' && isInterestingVertex(entity))) {
68910                         var hasDirections = entity.directions(graph, projection).length;
68911                         var markerPadding;
68912
68913                         if (!wireframe && geometry === 'point' && !(zoom >= 18 && hasDirections)) {
68914                             renderNodeAs[entity.id] = 'point';
68915                             markerPadding = 20;   // extra y for marker height
68916                         } else {
68917                             renderNodeAs[entity.id] = 'vertex';
68918                             markerPadding = 0;
68919                         }
68920
68921                         var coord = projection(entity.loc);
68922                         var nodePadding = 10;
68923                         var bbox = {
68924                             minX: coord[0] - nodePadding,
68925                             minY: coord[1] - nodePadding - markerPadding,
68926                             maxX: coord[0] + nodePadding,
68927                             maxY: coord[1] + nodePadding
68928                         };
68929
68930                         doInsert(bbox, entity.id + 'P');
68931                     }
68932
68933                     // From here on, treat vertices like points
68934                     if (geometry === 'vertex') {
68935                         geometry = 'point';
68936                     }
68937
68938                     // Determine which entities are label-able
68939                     var preset = geometry === 'area' && _mainPresetIndex.match(entity, graph);
68940                     var icon = preset && !shouldSkipIcon(preset) && preset.icon;
68941
68942                     if (!icon && !utilDisplayName(entity))
68943                         { continue; }
68944
68945                     for (k = 0; k < labelStack.length; k++) {
68946                         var matchGeom = labelStack[k][0];
68947                         var matchKey = labelStack[k][1];
68948                         var matchVal = labelStack[k][2];
68949                         var hasVal = entity.tags[matchKey];
68950
68951                         if (geometry === matchGeom && hasVal && (matchVal === '*' || matchVal === hasVal)) {
68952                             labelable[k].push(entity);
68953                             break;
68954                         }
68955                     }
68956                 }
68957
68958                 var positions = {
68959                     point: [],
68960                     line: [],
68961                     area: []
68962                 };
68963
68964                 var labelled = {
68965                     point: [],
68966                     line: [],
68967                     area: []
68968                 };
68969
68970                 // Try and find a valid label for labellable entities
68971                 for (k = 0; k < labelable.length; k++) {
68972                     var fontSize = labelStack[k][3];
68973
68974                     for (i = 0; i < labelable[k].length; i++) {
68975                         entity = labelable[k][i];
68976                         geometry = entity.geometry(graph);
68977
68978                         var getName = (geometry === 'line') ? utilDisplayNameForPath : utilDisplayName;
68979                         var name = getName(entity);
68980                         var width = name && textWidth(name, fontSize);
68981                         var p = null;
68982
68983                         if (geometry === 'point' || geometry === 'vertex') {
68984                             // no point or vertex labels in wireframe mode
68985                             // no vertex labels at low zooms (vertices have no icons)
68986                             if (wireframe) { continue; }
68987                             var renderAs = renderNodeAs[entity.id];
68988                             if (renderAs === 'vertex' && zoom < 17) { continue; }
68989
68990                             p = getPointLabel(entity, width, fontSize, renderAs);
68991
68992                         } else if (geometry === 'line') {
68993                             p = getLineLabel(entity, width, fontSize);
68994
68995                         } else if (geometry === 'area') {
68996                             p = getAreaLabel(entity, width, fontSize);
68997                         }
68998
68999                         if (p) {
69000                             if (geometry === 'vertex') { geometry = 'point'; }  // treat vertex like point
69001                             p.classes = geometry + ' tag-' + labelStack[k][1];
69002                             positions[geometry].push(p);
69003                             labelled[geometry].push(entity);
69004                         }
69005                     }
69006                 }
69007
69008
69009                 function isInterestingVertex(entity) {
69010                     var selectedIDs = context.selectedIDs();
69011
69012                     return entity.hasInterestingTags() ||
69013                         entity.isEndpoint(graph) ||
69014                         entity.isConnected(graph) ||
69015                         selectedIDs.indexOf(entity.id) !== -1 ||
69016                         graph.parentWays(entity).some(function(parent) {
69017                             return selectedIDs.indexOf(parent.id) !== -1;
69018                         });
69019                 }
69020
69021
69022                 function getPointLabel(entity, width, height, geometry) {
69023                     var y = (geometry === 'point' ? -12 : 0);
69024                     var pointOffsets = {
69025                         ltr: [15, y, 'start'],
69026                         rtl: [-15, y, 'end']
69027                     };
69028
69029                     var textDirection = _mainLocalizer.textDirection();
69030
69031                     var coord = projection(entity.loc);
69032                     var textPadding = 2;
69033                     var offset = pointOffsets[textDirection];
69034                     var p = {
69035                         height: height,
69036                         width: width,
69037                         x: coord[0] + offset[0],
69038                         y: coord[1] + offset[1],
69039                         textAnchor: offset[2]
69040                     };
69041
69042                     // insert a collision box for the text label..
69043                     var bbox;
69044                     if (textDirection === 'rtl') {
69045                         bbox = {
69046                             minX: p.x - width - textPadding,
69047                             minY: p.y - (height / 2) - textPadding,
69048                             maxX: p.x + textPadding,
69049                             maxY: p.y + (height / 2) + textPadding
69050                         };
69051                     } else {
69052                         bbox = {
69053                             minX: p.x - textPadding,
69054                             minY: p.y - (height / 2) - textPadding,
69055                             maxX: p.x + width + textPadding,
69056                             maxY: p.y + (height / 2) + textPadding
69057                         };
69058                     }
69059
69060                     if (tryInsert([bbox], entity.id, true)) {
69061                         return p;
69062                     }
69063                 }
69064
69065
69066                 function getLineLabel(entity, width, height) {
69067                     var viewport = geoExtent(context.projection.clipExtent()).polygon();
69068                     var points = graph.childNodes(entity)
69069                         .map(function(node) { return projection(node.loc); });
69070                     var length = geoPathLength(points);
69071
69072                     if (length < width + 20) { return; }
69073
69074                     // % along the line to attempt to place the label
69075                     var lineOffsets = [50, 45, 55, 40, 60, 35, 65, 30, 70,
69076                                        25, 75, 20, 80, 15, 95, 10, 90, 5, 95];
69077                     var padding = 3;
69078
69079                     for (var i = 0; i < lineOffsets.length; i++) {
69080                         var offset = lineOffsets[i];
69081                         var middle = offset / 100 * length;
69082                         var start = middle - width / 2;
69083
69084                         if (start < 0 || start + width > length) { continue; }
69085
69086                         // generate subpath and ignore paths that are invalid or don't cross viewport.
69087                         var sub = subpath(points, start, start + width);
69088                         if (!sub || !geoPolygonIntersectsPolygon(viewport, sub, true)) {
69089                             continue;
69090                         }
69091
69092                         var isReverse = reverse(sub);
69093                         if (isReverse) {
69094                             sub = sub.reverse();
69095                         }
69096
69097                         var bboxes = [];
69098                         var boxsize = (height + 2) / 2;
69099
69100                         for (var j = 0; j < sub.length - 1; j++) {
69101                             var a = sub[j];
69102                             var b = sub[j + 1];
69103
69104                             // split up the text into small collision boxes
69105                             var num = Math.max(1, Math.floor(geoVecLength(a, b) / boxsize / 2));
69106
69107                             for (var box = 0; box < num; box++) {
69108                                 var p = geoVecInterp(a, b, box / num);
69109                                 var x0 = p[0] - boxsize - padding;
69110                                 var y0 = p[1] - boxsize - padding;
69111                                 var x1 = p[0] + boxsize + padding;
69112                                 var y1 = p[1] + boxsize + padding;
69113
69114                                 bboxes.push({
69115                                     minX: Math.min(x0, x1),
69116                                     minY: Math.min(y0, y1),
69117                                     maxX: Math.max(x0, x1),
69118                                     maxY: Math.max(y0, y1)
69119                                 });
69120                             }
69121                         }
69122
69123                         if (tryInsert(bboxes, entity.id, false)) {   // accept this one
69124                             return {
69125                                 'font-size': height + 2,
69126                                 lineString: lineString(sub),
69127                                 startOffset: offset + '%'
69128                             };
69129                         }
69130                     }
69131
69132                     function reverse(p) {
69133                         var angle = Math.atan2(p[1][1] - p[0][1], p[1][0] - p[0][0]);
69134                         return !(p[0][0] < p[p.length - 1][0] && angle < Math.PI/2 && angle > -Math.PI/2);
69135                     }
69136
69137                     function lineString(points) {
69138                         return 'M' + points.join('L');
69139                     }
69140
69141                     function subpath(points, from, to) {
69142                         var sofar = 0;
69143                         var start, end, i0, i1;
69144
69145                         for (var i = 0; i < points.length - 1; i++) {
69146                             var a = points[i];
69147                             var b = points[i + 1];
69148                             var current = geoVecLength(a, b);
69149                             var portion;
69150                             if (!start && sofar + current >= from) {
69151                                 portion = (from - sofar) / current;
69152                                 start = [
69153                                     a[0] + portion * (b[0] - a[0]),
69154                                     a[1] + portion * (b[1] - a[1])
69155                                 ];
69156                                 i0 = i + 1;
69157                             }
69158                             if (!end && sofar + current >= to) {
69159                                 portion = (to - sofar) / current;
69160                                 end = [
69161                                     a[0] + portion * (b[0] - a[0]),
69162                                     a[1] + portion * (b[1] - a[1])
69163                                 ];
69164                                 i1 = i + 1;
69165                             }
69166                             sofar += current;
69167                         }
69168
69169                         var result = points.slice(i0, i1);
69170                         result.unshift(start);
69171                         result.push(end);
69172                         return result;
69173                     }
69174                 }
69175
69176
69177                 function getAreaLabel(entity, width, height) {
69178                     var centroid = path.centroid(entity.asGeoJSON(graph, true));
69179                     var extent = entity.extent(graph);
69180                     var areaWidth = projection(extent[1])[0] - projection(extent[0])[0];
69181
69182                     if (isNaN(centroid[0]) || areaWidth < 20) { return; }
69183
69184                     var preset = _mainPresetIndex.match(entity, context.graph());
69185                     var picon = preset && preset.icon;
69186                     var iconSize = 17;
69187                     var padding = 2;
69188                     var p = {};
69189
69190                     if (picon) {  // icon and label..
69191                         if (addIcon()) {
69192                             addLabel(iconSize + padding);
69193                             return p;
69194                         }
69195                     } else {   // label only..
69196                         if (addLabel(0)) {
69197                             return p;
69198                         }
69199                     }
69200
69201
69202                     function addIcon() {
69203                         var iconX = centroid[0] - (iconSize / 2);
69204                         var iconY = centroid[1] - (iconSize / 2);
69205                         var bbox = {
69206                             minX: iconX,
69207                             minY: iconY,
69208                             maxX: iconX + iconSize,
69209                             maxY: iconY + iconSize
69210                         };
69211
69212                         if (tryInsert([bbox], entity.id + 'I', true)) {
69213                             p.transform = 'translate(' + iconX + ',' + iconY + ')';
69214                             return true;
69215                         }
69216                         return false;
69217                     }
69218
69219                     function addLabel(yOffset) {
69220                         if (width && areaWidth >= width + 20) {
69221                             var labelX = centroid[0];
69222                             var labelY = centroid[1] + yOffset;
69223                             var bbox = {
69224                                 minX: labelX - (width / 2) - padding,
69225                                 minY: labelY - (height / 2) - padding,
69226                                 maxX: labelX + (width / 2) + padding,
69227                                 maxY: labelY + (height / 2) + padding
69228                             };
69229
69230                             if (tryInsert([bbox], entity.id, true)) {
69231                                 p.x = labelX;
69232                                 p.y = labelY;
69233                                 p.textAnchor = 'middle';
69234                                 p.height = height;
69235                                 return true;
69236                             }
69237                         }
69238                         return false;
69239                     }
69240                 }
69241
69242
69243                 // force insert a singular bounding box
69244                 // singular box only, no array, id better be unique
69245                 function doInsert(bbox, id) {
69246                     bbox.id = id;
69247
69248                     var oldbox = _entitybboxes[id];
69249                     if (oldbox) {
69250                         _rdrawn.remove(oldbox);
69251                     }
69252                     _entitybboxes[id] = bbox;
69253                     _rdrawn.insert(bbox);
69254                 }
69255
69256
69257                 function tryInsert(bboxes, id, saveSkipped) {
69258                     var skipped = false;
69259
69260                     for (var i = 0; i < bboxes.length; i++) {
69261                         var bbox = bboxes[i];
69262                         bbox.id = id;
69263
69264                         // Check that label is visible
69265                         if (bbox.minX < 0 || bbox.minY < 0 || bbox.maxX > dimensions[0] || bbox.maxY > dimensions[1]) {
69266                             skipped = true;
69267                             break;
69268                         }
69269                         if (_rdrawn.collides(bbox)) {
69270                             skipped = true;
69271                             break;
69272                         }
69273                     }
69274
69275                     _entitybboxes[id] = bboxes;
69276
69277                     if (skipped) {
69278                         if (saveSkipped) {
69279                             _rskipped.load(bboxes);
69280                         }
69281                     } else {
69282                         _rdrawn.load(bboxes);
69283                     }
69284
69285                     return !skipped;
69286                 }
69287
69288
69289                 var layer = selection.selectAll('.layer-osm.labels');
69290                 layer.selectAll('.labels-group')
69291                     .data(['halo', 'label', 'debug'])
69292                     .enter()
69293                     .append('g')
69294                     .attr('class', function(d) { return 'labels-group ' + d; });
69295
69296                 var halo = layer.selectAll('.labels-group.halo');
69297                 var label = layer.selectAll('.labels-group.label');
69298                 var debug = layer.selectAll('.labels-group.debug');
69299
69300                 // points
69301                 drawPointLabels(label, labelled.point, filter, 'pointlabel', positions.point);
69302                 drawPointLabels(halo, labelled.point, filter, 'pointlabel-halo', positions.point);
69303
69304                 // lines
69305                 drawLinePaths(layer, labelled.line, filter, '', positions.line);
69306                 drawLineLabels(label, labelled.line, filter, 'linelabel', positions.line);
69307                 drawLineLabels(halo, labelled.line, filter, 'linelabel-halo', positions.line);
69308
69309                 // areas
69310                 drawAreaLabels(label, labelled.area, filter, 'arealabel', positions.area);
69311                 drawAreaLabels(halo, labelled.area, filter, 'arealabel-halo', positions.area);
69312                 drawAreaIcons(label, labelled.area, filter, 'areaicon', positions.area);
69313                 drawAreaIcons(halo, labelled.area, filter, 'areaicon-halo', positions.area);
69314
69315                 // debug
69316                 drawCollisionBoxes(debug, _rskipped, 'debug-skipped');
69317                 drawCollisionBoxes(debug, _rdrawn, 'debug-drawn');
69318
69319                 layer.call(filterLabels);
69320             }
69321
69322
69323             function filterLabels(selection) {
69324                 var drawLayer = selection.selectAll('.layer-osm.labels');
69325                 var layers = drawLayer.selectAll('.labels-group.halo, .labels-group.label');
69326
69327                 layers.selectAll('.nolabel')
69328                     .classed('nolabel', false);
69329
69330                 var mouse = context.map().mouse();
69331                 var graph = context.graph();
69332                 var selectedIDs = context.selectedIDs();
69333                 var ids = [];
69334                 var pad, bbox;
69335
69336                 // hide labels near the mouse
69337                 if (mouse) {
69338                     pad = 20;
69339                     bbox = { minX: mouse[0] - pad, minY: mouse[1] - pad, maxX: mouse[0] + pad, maxY: mouse[1] + pad };
69340                     var nearMouse = _rdrawn.search(bbox).map(function(entity) { return entity.id; });
69341                     ids.push.apply(ids, nearMouse);
69342                 }
69343
69344                 // hide labels on selected nodes (they look weird when dragging / haloed)
69345                 for (var i = 0; i < selectedIDs.length; i++) {
69346                     var entity = graph.hasEntity(selectedIDs[i]);
69347                     if (entity && entity.type === 'node') {
69348                         ids.push(selectedIDs[i]);
69349                     }
69350                 }
69351
69352                 layers.selectAll(utilEntitySelector(ids))
69353                     .classed('nolabel', true);
69354
69355
69356                 // draw the mouse bbox if debugging is on..
69357                 var debug = selection.selectAll('.labels-group.debug');
69358                 var gj = [];
69359                 if (context.getDebug('collision')) {
69360                     gj = bbox ? [{
69361                         type: 'Polygon',
69362                         coordinates: [[
69363                             [bbox.minX, bbox.minY],
69364                             [bbox.maxX, bbox.minY],
69365                             [bbox.maxX, bbox.maxY],
69366                             [bbox.minX, bbox.maxY],
69367                             [bbox.minX, bbox.minY]
69368                         ]]
69369                     }] : [];
69370                 }
69371
69372                 var box = debug.selectAll('.debug-mouse')
69373                     .data(gj);
69374
69375                 // exit
69376                 box.exit()
69377                     .remove();
69378
69379                 // enter/update
69380                 box.enter()
69381                     .append('path')
69382                     .attr('class', 'debug debug-mouse yellow')
69383                     .merge(box)
69384                     .attr('d', d3_geoPath());
69385             }
69386
69387
69388             var throttleFilterLabels = throttle(filterLabels, 100);
69389
69390
69391             drawLabels.observe = function(selection) {
69392                 var listener = function() { throttleFilterLabels(selection); };
69393                 selection.on('mousemove.hidelabels', listener);
69394                 context.on('enter.hidelabels', listener);
69395             };
69396
69397
69398             drawLabels.off = function(selection) {
69399                 throttleFilterLabels.cancel();
69400                 selection.on('mousemove.hidelabels', null);
69401                 context.on('enter.hidelabels', null);
69402             };
69403
69404
69405             return drawLabels;
69406         }
69407
69408         var _layerEnabled$1 = false;
69409         var _qaService$1;
69410
69411         function svgImproveOSM(projection, context, dispatch) {
69412           var throttledRedraw = throttle(function () { return dispatch.call('change'); }, 1000);
69413           var minZoom = 12;
69414
69415           var touchLayer = select(null);
69416           var drawLayer = select(null);
69417           var layerVisible = false;
69418
69419           function markerPath(selection, klass) {
69420             selection
69421               .attr('class', klass)
69422               .attr('transform', 'translate(-10, -28)')
69423               .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');
69424           }
69425
69426           // Loosely-coupled improveOSM service for fetching issues
69427           function getService() {
69428             if (services.improveOSM && !_qaService$1) {
69429               _qaService$1 = services.improveOSM;
69430               _qaService$1.on('loaded', throttledRedraw);
69431             } else if (!services.improveOSM && _qaService$1) {
69432               _qaService$1 = null;
69433             }
69434
69435             return _qaService$1;
69436           }
69437
69438           // Show the markers
69439           function editOn() {
69440             if (!layerVisible) {
69441               layerVisible = true;
69442               drawLayer
69443                 .style('display', 'block');
69444             }
69445           }
69446
69447           // Immediately remove the markers and their touch targets
69448           function editOff() {
69449             if (layerVisible) {
69450               layerVisible = false;
69451               drawLayer
69452                 .style('display', 'none');
69453               drawLayer.selectAll('.qaItem.improveOSM')
69454                 .remove();
69455               touchLayer.selectAll('.qaItem.improveOSM')
69456                 .remove();
69457             }
69458           }
69459
69460           // Enable the layer.  This shows the markers and transitions them to visible.
69461           function layerOn() {
69462             editOn();
69463
69464             drawLayer
69465               .style('opacity', 0)
69466               .transition()
69467               .duration(250)
69468               .style('opacity', 1)
69469               .on('end interrupt', function () { return dispatch.call('change'); });
69470           }
69471
69472           // Disable the layer.  This transitions the layer invisible and then hides the markers.
69473           function layerOff() {
69474             throttledRedraw.cancel();
69475             drawLayer.interrupt();
69476             touchLayer.selectAll('.qaItem.improveOSM')
69477               .remove();
69478
69479             drawLayer
69480               .transition()
69481               .duration(250)
69482               .style('opacity', 0)
69483               .on('end interrupt', function () {
69484                 editOff();
69485                 dispatch.call('change');
69486               });
69487           }
69488
69489           // Update the issue markers
69490           function updateMarkers() {
69491             if (!layerVisible || !_layerEnabled$1) { return; }
69492
69493             var service = getService();
69494             var selectedID = context.selectedErrorID();
69495             var data = (service ? service.getItems(projection) : []);
69496             var getTransform = svgPointTransform(projection);
69497
69498             // Draw markers..
69499             var markers = drawLayer.selectAll('.qaItem.improveOSM')
69500               .data(data, function (d) { return d.id; });
69501
69502             // exit
69503             markers.exit()
69504               .remove();
69505
69506             // enter
69507             var markersEnter = markers.enter()
69508               .append('g')
69509                 .attr('class', function (d) { return ("qaItem " + (d.service) + " itemId-" + (d.id) + " itemType-" + (d.itemType)); });
69510
69511             markersEnter
69512               .append('polygon')
69513                 .call(markerPath, 'shadow');
69514
69515             markersEnter
69516               .append('ellipse')
69517                 .attr('cx', 0)
69518                 .attr('cy', 0)
69519                 .attr('rx', 4.5)
69520                 .attr('ry', 2)
69521                 .attr('class', 'stroke');
69522
69523             markersEnter
69524               .append('polygon')
69525                 .attr('fill', 'currentColor')
69526                 .call(markerPath, 'qaItem-fill');
69527
69528             markersEnter
69529               .append('use')
69530                 .attr('transform', 'translate(-6.5, -23)')
69531                 .attr('class', 'icon-annotation')
69532                 .attr('width', '13px')
69533                 .attr('height', '13px')
69534                 .attr('xlink:href', function (d) {
69535                   var picon = d.icon;
69536
69537                   if (!picon) {
69538                   return '';
69539                   } else {
69540                   var isMaki = /^maki-/.test(picon);
69541                   return ("#" + picon + (isMaki ? '-11' : ''));
69542                   }
69543                 });
69544
69545             // update
69546             markers
69547               .merge(markersEnter)
69548               .sort(sortY)
69549                 .classed('selected', function (d) { return d.id === selectedID; })
69550                 .attr('transform', getTransform);
69551
69552
69553             // Draw targets..
69554             if (touchLayer.empty()) { return; }
69555             var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
69556
69557             var targets = touchLayer.selectAll('.qaItem.improveOSM')
69558               .data(data, function (d) { return d.id; });
69559
69560             // exit
69561             targets.exit()
69562               .remove();
69563
69564             // enter/update
69565             targets.enter()
69566               .append('rect')
69567                 .attr('width', '20px')
69568                 .attr('height', '30px')
69569                 .attr('x', '-10px')
69570                 .attr('y', '-28px')
69571               .merge(targets)
69572               .sort(sortY)
69573                 .attr('class', function (d) { return ("qaItem " + (d.service) + " target " + fillClass + " itemId-" + (d.id)); })
69574                 .attr('transform', getTransform);
69575
69576             function sortY(a, b) {
69577               return (a.id === selectedID) ? 1
69578                 : (b.id === selectedID) ? -1
69579                 : b.loc[1] - a.loc[1];
69580             }
69581           }
69582
69583           // Draw the ImproveOSM layer and schedule loading issues and updating markers.
69584           function drawImproveOSM(selection) {
69585             var service = getService();
69586
69587             var surface = context.surface();
69588             if (surface && !surface.empty()) {
69589               touchLayer = surface.selectAll('.data-layer.touch .layer-touch.markers');
69590             }
69591
69592             drawLayer = selection.selectAll('.layer-improveOSM')
69593               .data(service ? [0] : []);
69594
69595             drawLayer.exit()
69596               .remove();
69597
69598             drawLayer = drawLayer.enter()
69599               .append('g')
69600                 .attr('class', 'layer-improveOSM')
69601                 .style('display', _layerEnabled$1 ? 'block' : 'none')
69602               .merge(drawLayer);
69603
69604             if (_layerEnabled$1) {
69605               if (service && ~~context.map().zoom() >= minZoom) {
69606                 editOn();
69607                 service.loadIssues(projection);
69608                 updateMarkers();
69609               } else {
69610                 editOff();
69611               }
69612             }
69613           }
69614
69615           // Toggles the layer on and off
69616           drawImproveOSM.enabled = function(val) {
69617             if (!arguments.length) { return _layerEnabled$1; }
69618
69619             _layerEnabled$1 = val;
69620             if (_layerEnabled$1) {
69621               layerOn();
69622             } else {
69623               layerOff();
69624               if (context.selectedErrorID()) {
69625                 context.enter(modeBrowse(context));
69626               }
69627             }
69628
69629             dispatch.call('change');
69630             return this;
69631           };
69632
69633           drawImproveOSM.supported = function () { return !!getService(); };
69634
69635           return drawImproveOSM;
69636         }
69637
69638         var _layerEnabled$2 = false;
69639         var _qaService$2;
69640
69641         function svgOsmose(projection, context, dispatch) {
69642           var throttledRedraw = throttle(function () { return dispatch.call('change'); }, 1000);
69643           var minZoom = 12;
69644
69645           var touchLayer = select(null);
69646           var drawLayer = select(null);
69647           var layerVisible = false;
69648
69649           function markerPath(selection, klass) {
69650             selection
69651               .attr('class', klass)
69652               .attr('transform', 'translate(-10, -28)')
69653               .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');
69654           }
69655
69656           // Loosely-coupled osmose service for fetching issues
69657           function getService() {
69658             if (services.osmose && !_qaService$2) {
69659               _qaService$2 = services.osmose;
69660               _qaService$2.on('loaded', throttledRedraw);
69661             } else if (!services.osmose && _qaService$2) {
69662               _qaService$2 = null;
69663             }
69664
69665             return _qaService$2;
69666           }
69667
69668           // Show the markers
69669           function editOn() {
69670             if (!layerVisible) {
69671               layerVisible = true;
69672               drawLayer
69673                 .style('display', 'block');
69674             }
69675           }
69676
69677           // Immediately remove the markers and their touch targets
69678           function editOff() {
69679             if (layerVisible) {
69680               layerVisible = false;
69681               drawLayer
69682                 .style('display', 'none');
69683               drawLayer.selectAll('.qaItem.osmose')
69684                 .remove();
69685               touchLayer.selectAll('.qaItem.osmose')
69686                 .remove();
69687             }
69688           }
69689
69690           // Enable the layer.  This shows the markers and transitions them to visible.
69691           function layerOn() {
69692             editOn();
69693
69694             drawLayer
69695               .style('opacity', 0)
69696               .transition()
69697               .duration(250)
69698               .style('opacity', 1)
69699               .on('end interrupt', function () { return dispatch.call('change'); });
69700           }
69701
69702           // Disable the layer.  This transitions the layer invisible and then hides the markers.
69703           function layerOff() {
69704             throttledRedraw.cancel();
69705             drawLayer.interrupt();
69706             touchLayer.selectAll('.qaItem.osmose')
69707               .remove();
69708
69709             drawLayer
69710               .transition()
69711               .duration(250)
69712               .style('opacity', 0)
69713               .on('end interrupt', function () {
69714                 editOff();
69715                 dispatch.call('change');
69716               });
69717           }
69718
69719           // Update the issue markers
69720           function updateMarkers() {
69721             if (!layerVisible || !_layerEnabled$2) { return; }
69722
69723             var service = getService();
69724             var selectedID = context.selectedErrorID();
69725             var data = (service ? service.getItems(projection) : []);
69726             var getTransform = svgPointTransform(projection);
69727
69728             // Draw markers..
69729             var markers = drawLayer.selectAll('.qaItem.osmose')
69730               .data(data, function (d) { return d.id; });
69731
69732             // exit
69733             markers.exit()
69734               .remove();
69735
69736             // enter
69737             var markersEnter = markers.enter()
69738               .append('g')
69739                 .attr('class', function (d) { return ("qaItem " + (d.service) + " itemId-" + (d.id) + " itemType-" + (d.itemType)); });
69740
69741             markersEnter
69742               .append('polygon')
69743                 .call(markerPath, 'shadow');
69744
69745             markersEnter
69746               .append('ellipse')
69747                 .attr('cx', 0)
69748                 .attr('cy', 0)
69749                 .attr('rx', 4.5)
69750                 .attr('ry', 2)
69751                 .attr('class', 'stroke');
69752
69753             markersEnter
69754               .append('polygon')
69755                 .attr('fill', function (d) { return service.getColor(d.item); })
69756                 .call(markerPath, 'qaItem-fill');
69757
69758             markersEnter
69759               .append('use')
69760                 .attr('transform', 'translate(-6.5, -23)')
69761                 .attr('class', 'icon-annotation')
69762                 .attr('width', '13px')
69763                 .attr('height', '13px')
69764                 .attr('xlink:href', function (d) {
69765                   var picon = d.icon;
69766
69767                   if (!picon) {
69768                     return '';
69769                   } else {
69770                     var isMaki = /^maki-/.test(picon);
69771                     return ("#" + picon + (isMaki ? '-11' : ''));
69772                   }
69773                 });
69774
69775             // update
69776             markers
69777               .merge(markersEnter)
69778               .sort(sortY)
69779                 .classed('selected', function (d) { return d.id === selectedID; })
69780                 .attr('transform', getTransform);
69781
69782             // Draw targets..
69783             if (touchLayer.empty()) { return; }
69784             var fillClass = context.getDebug('target') ? 'pink' : 'nocolor';
69785
69786             var targets = touchLayer.selectAll('.qaItem.osmose')
69787               .data(data, function (d) { return d.id; });
69788
69789             // exit
69790             targets.exit()
69791               .remove();
69792
69793             // enter/update
69794             targets.enter()
69795               .append('rect')
69796                 .attr('width', '20px')
69797                 .attr('height', '30px')
69798                 .attr('x', '-10px')
69799                 .attr('y', '-28px')
69800               .merge(targets)
69801               .sort(sortY)
69802                 .attr('class', function (d) { return ("qaItem " + (d.service) + " target " + fillClass + " itemId-" + (d.id)); })
69803                 .attr('transform', getTransform);
69804
69805             function sortY(a, b) {
69806               return (a.id === selectedID) ? 1
69807                 : (b.id === selectedID) ? -1
69808                 : b.loc[1] - a.loc[1];
69809             }
69810           }
69811
69812           // Draw the Osmose layer and schedule loading issues and updating markers.
69813           function drawOsmose(selection) {
69814             var service = getService();
69815
69816             var surface = context.surface();
69817             if (surface && !surface.empty()) {
69818               touchLayer = surface.selectAll('.data-layer.touch .layer-touch.markers');
69819             }
69820
69821             drawLayer = selection.selectAll('.layer-osmose')
69822               .data(service ? [0] : []);
69823
69824             drawLayer.exit()
69825               .remove();
69826
69827             drawLayer = drawLayer.enter()
69828               .append('g')
69829                 .attr('class', 'layer-osmose')
69830                 .style('display', _layerEnabled$2 ? 'block' : 'none')
69831               .merge(drawLayer);
69832
69833             if (_layerEnabled$2) {
69834               if (service && ~~context.map().zoom() >= minZoom) {
69835                 editOn();
69836                 service.loadIssues(projection);
69837                 updateMarkers();
69838               } else {
69839                 editOff();
69840               }
69841             }
69842           }
69843
69844           // Toggles the layer on and off
69845           drawOsmose.enabled = function(val) {
69846             if (!arguments.length) { return _layerEnabled$2; }
69847
69848             _layerEnabled$2 = val;
69849             if (_layerEnabled$2) {
69850               // Strings supplied by Osmose fetched before showing layer for first time
69851               // NOTE: Currently no way to change locale in iD at runtime, would need to re-call this method if that's ever implemented
69852               // Also, If layer is toggled quickly multiple requests are sent
69853               getService().loadStrings()
69854                 .then(layerOn)
69855                 .catch(function (err) {
69856                   console.log(err); // eslint-disable-line no-console
69857                 });
69858             } else {
69859               layerOff();
69860               if (context.selectedErrorID()) {
69861                 context.enter(modeBrowse(context));
69862               }
69863             }
69864
69865             dispatch.call('change');
69866             return this;
69867           };
69868
69869           drawOsmose.supported = function () { return !!getService(); };
69870
69871           return drawOsmose;
69872         }
69873
69874         function svgStreetside(projection, context, dispatch) {
69875             var throttledRedraw = throttle(function () { dispatch.call('change'); }, 1000);
69876             var minZoom = 14;
69877             var minMarkerZoom = 16;
69878             var minViewfieldZoom = 18;
69879             var layer = select(null);
69880             var _viewerYaw = 0;
69881             var _selectedSequence = null;
69882             var _streetside;
69883
69884             /**
69885              * init().
69886              */
69887             function init() {
69888                 if (svgStreetside.initialized) { return; }  // run once
69889                 svgStreetside.enabled = false;
69890                 svgStreetside.initialized = true;
69891             }
69892
69893             /**
69894              * getService().
69895              */
69896             function getService() {
69897                 if (services.streetside && !_streetside) {
69898                     _streetside = services.streetside;
69899                     _streetside.event
69900                         .on('viewerChanged.svgStreetside', viewerChanged)
69901                         .on('loadedBubbles.svgStreetside', throttledRedraw);
69902                 } else if (!services.streetside && _streetside) {
69903                     _streetside = null;
69904                 }
69905
69906                 return _streetside;
69907             }
69908
69909             /**
69910              * showLayer().
69911              */
69912             function showLayer() {
69913                 var service = getService();
69914                 if (!service) { return; }
69915
69916                 editOn();
69917
69918                 layer
69919                     .style('opacity', 0)
69920                     .transition()
69921                     .duration(250)
69922                     .style('opacity', 1)
69923                     .on('end', function () { dispatch.call('change'); });
69924             }
69925
69926             /**
69927              * hideLayer().
69928              */
69929             function hideLayer() {
69930                 throttledRedraw.cancel();
69931
69932                 layer
69933                     .transition()
69934                     .duration(250)
69935                     .style('opacity', 0)
69936                     .on('end', editOff);
69937             }
69938
69939             /**
69940              * editOn().
69941              */
69942             function editOn() {
69943                 layer.style('display', 'block');
69944             }
69945
69946             /**
69947              * editOff().
69948              */
69949             function editOff() {
69950                 layer.selectAll('.viewfield-group').remove();
69951                 layer.style('display', 'none');
69952             }
69953
69954             /**
69955              * click() Handles 'bubble' point click event.
69956              */
69957             function click(d) {
69958                 var service = getService();
69959                 if (!service) { return; }
69960
69961                 // try to preserve the viewer rotation when staying on the same sequence
69962                 if (d.sequenceKey !== _selectedSequence) {
69963                     _viewerYaw = 0;  // reset
69964                 }
69965                 _selectedSequence = d.sequenceKey;
69966
69967                 service
69968                     .selectImage(context, d)
69969                     .then(function (response) {
69970                         if (response.status === 'ok'){
69971                             service.showViewer(context, _viewerYaw);
69972                         }
69973                     });
69974
69975
69976                 context.map().centerEase(d.loc);
69977             }
69978
69979             /**
69980              * mouseover().
69981              */
69982             function mouseover(d) {
69983                 var service = getService();
69984                 if (service) { service.setStyles(context, d); }
69985             }
69986
69987             /**
69988              * mouseout().
69989              */
69990             function mouseout() {
69991                 var service = getService();
69992                 if (service) { service.setStyles(context, null); }
69993             }
69994
69995             /**
69996              * transform().
69997              */
69998             function transform(d) {
69999                 var t = svgPointTransform(projection)(d);
70000                 var rot = d.ca + _viewerYaw;
70001                 if (rot) {
70002                     t += ' rotate(' + Math.floor(rot) + ',0,0)';
70003                 }
70004                 return t;
70005             }
70006
70007
70008             function viewerChanged() {
70009                 var service = getService();
70010                 if (!service) { return; }
70011
70012                 var viewer = service.viewer();
70013                 if (!viewer) { return; }
70014
70015                 // update viewfield rotation
70016                 _viewerYaw = viewer.getYaw();
70017
70018                 // avoid updating if the map is currently transformed
70019                 // e.g. during drags or easing.
70020                 if (context.map().isTransformed()) { return; }
70021
70022                 layer.selectAll('.viewfield-group.currentView')
70023                     .attr('transform', transform);
70024             }
70025
70026
70027             context.photos().on('change.streetside', update);
70028
70029             /**
70030              * update().
70031              */
70032             function update() {
70033                 var viewer = context.container().select('.photoviewer');
70034                 var selected = viewer.empty() ? undefined : viewer.datum();
70035                 var z = ~~context.map().zoom();
70036                 var showMarkers = (z >= minMarkerZoom);
70037                 var showViewfields = (z >= minViewfieldZoom);
70038                 var service = getService();
70039
70040                 var sequences = [];
70041                 var bubbles = [];
70042
70043                 if (context.photos().showsPanoramic()) {
70044                     sequences = (service ? service.sequences(projection) : []);
70045                     bubbles = (service && showMarkers ? service.bubbles(projection) : []);
70046                 }
70047
70048                 var traces = layer.selectAll('.sequences').selectAll('.sequence')
70049                     .data(sequences, function(d) { return d.properties.key; });
70050
70051                 // exit
70052                 traces.exit()
70053                     .remove();
70054
70055                 // enter/update
70056                 traces = traces.enter()
70057                     .append('path')
70058                     .attr('class', 'sequence')
70059                     .merge(traces)
70060                     .attr('d', svgPath(projection).geojson);
70061
70062
70063                 var groups = layer.selectAll('.markers').selectAll('.viewfield-group')
70064                     .data(bubbles, function(d) {
70065                         // force reenter once bubbles are attached to a sequence
70066                         return d.key + (d.sequenceKey ? 'v1' : 'v0');
70067                     });
70068
70069                 // exit
70070                 groups.exit()
70071                     .remove();
70072
70073                 // enter
70074                 var groupsEnter = groups.enter()
70075                     .append('g')
70076                     .attr('class', 'viewfield-group')
70077                     .on('mouseenter', mouseover)
70078                     .on('mouseleave', mouseout)
70079                     .on('click', click);
70080
70081                 groupsEnter
70082                     .append('g')
70083                     .attr('class', 'viewfield-scale');
70084
70085                 // update
70086                 var markers = groups
70087                     .merge(groupsEnter)
70088                     .sort(function(a, b) {
70089                         return (a === selected) ? 1
70090                             : (b === selected) ? -1
70091                             : b.loc[1] - a.loc[1];
70092                     })
70093                     .attr('transform', transform)
70094                     .select('.viewfield-scale');
70095
70096
70097                 markers.selectAll('circle')
70098                     .data([0])
70099                     .enter()
70100                     .append('circle')
70101                     .attr('dx', '0')
70102                     .attr('dy', '0')
70103                     .attr('r', '6');
70104
70105                 var viewfields = markers.selectAll('.viewfield')
70106                     .data(showViewfields ? [0] : []);
70107
70108                 viewfields.exit()
70109                     .remove();
70110
70111                 // viewfields may or may not be drawn...
70112                 // but if they are, draw below the circles
70113                 viewfields.enter()
70114                     .insert('path', 'circle')
70115                     .attr('class', 'viewfield')
70116                     .attr('transform', 'scale(1.5,1.5),translate(-8, -13)')
70117                     .attr('d', viewfieldPath);
70118
70119                 function viewfieldPath() {
70120                     var d = this.parentNode.__data__;
70121                     if (d.pano) {
70122                         return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0';
70123                     } else {
70124                         return 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z';
70125                     }
70126                 }
70127
70128             }
70129
70130             /**
70131              * drawImages()
70132              * drawImages is the method that is returned (and that runs) everytime 'svgStreetside()' is called.
70133              * 'svgStreetside()' is called from index.js
70134              */
70135             function drawImages(selection) {
70136                 var enabled = svgStreetside.enabled;
70137                 var service = getService();
70138
70139                 layer = selection.selectAll('.layer-streetside-images')
70140                     .data(service ? [0] : []);
70141
70142                 layer.exit()
70143                     .remove();
70144
70145                 var layerEnter = layer.enter()
70146                     .append('g')
70147                     .attr('class', 'layer-streetside-images')
70148                     .style('display', enabled ? 'block' : 'none');
70149
70150                 layerEnter
70151                     .append('g')
70152                     .attr('class', 'sequences');
70153
70154                 layerEnter
70155                     .append('g')
70156                     .attr('class', 'markers');
70157
70158                 layer = layerEnter
70159                     .merge(layer);
70160
70161                 if (enabled) {
70162                     if (service && ~~context.map().zoom() >= minZoom) {
70163                         editOn();
70164                         update();
70165                         service.loadBubbles(projection);
70166                     } else {
70167                         editOff();
70168                     }
70169                 }
70170             }
70171
70172
70173             /**
70174              * drawImages.enabled().
70175              */
70176             drawImages.enabled = function(_) {
70177                 if (!arguments.length) { return svgStreetside.enabled; }
70178                 svgStreetside.enabled = _;
70179                 if (svgStreetside.enabled) {
70180                     showLayer();
70181                 } else {
70182                     hideLayer();
70183                 }
70184                 dispatch.call('change');
70185                 return this;
70186             };
70187
70188             /**
70189              * drawImages.supported().
70190              */
70191             drawImages.supported = function() {
70192                 return !!getService();
70193             };
70194
70195             init();
70196
70197             return drawImages;
70198         }
70199
70200         function svgMapillaryImages(projection, context, dispatch) {
70201             var throttledRedraw = throttle(function () { dispatch.call('change'); }, 1000);
70202             var minZoom = 12;
70203             var minMarkerZoom = 16;
70204             var minViewfieldZoom = 18;
70205             var layer = select(null);
70206             var _mapillary;
70207             var viewerCompassAngle;
70208
70209
70210             function init() {
70211                 if (svgMapillaryImages.initialized) { return; }  // run once
70212                 svgMapillaryImages.enabled = false;
70213                 svgMapillaryImages.initialized = true;
70214             }
70215
70216
70217             function getService() {
70218                 if (services.mapillary && !_mapillary) {
70219                     _mapillary = services.mapillary;
70220                     _mapillary.event.on('loadedImages', throttledRedraw);
70221                     _mapillary.event.on('bearingChanged', function(e) {
70222                         viewerCompassAngle = e;
70223
70224                         // avoid updating if the map is currently transformed
70225                         // e.g. during drags or easing.
70226                         if (context.map().isTransformed()) { return; }
70227
70228                         layer.selectAll('.viewfield-group.currentView')
70229                             .filter(function(d) {
70230                                 return d.pano;
70231                             })
70232                             .attr('transform', transform);
70233                     });
70234                 } else if (!services.mapillary && _mapillary) {
70235                     _mapillary = null;
70236                 }
70237
70238                 return _mapillary;
70239             }
70240
70241
70242             function showLayer() {
70243                 var service = getService();
70244                 if (!service) { return; }
70245
70246                 editOn();
70247
70248                 layer
70249                     .style('opacity', 0)
70250                     .transition()
70251                     .duration(250)
70252                     .style('opacity', 1)
70253                     .on('end', function () { dispatch.call('change'); });
70254             }
70255
70256
70257             function hideLayer() {
70258                 throttledRedraw.cancel();
70259
70260                 layer
70261                     .transition()
70262                     .duration(250)
70263                     .style('opacity', 0)
70264                     .on('end', editOff);
70265             }
70266
70267
70268             function editOn() {
70269                 layer.style('display', 'block');
70270             }
70271
70272
70273             function editOff() {
70274                 layer.selectAll('.viewfield-group').remove();
70275                 layer.style('display', 'none');
70276             }
70277
70278
70279             function click(d) {
70280                 var service = getService();
70281                 if (!service) { return; }
70282
70283                 service
70284                     .selectImage(context, d.key)
70285                     .updateViewer(context, d.key)
70286                     .showViewer(context);
70287
70288                 context.map().centerEase(d.loc);
70289             }
70290
70291
70292             function mouseover(d) {
70293                 var service = getService();
70294                 if (service) { service.setStyles(context, d); }
70295             }
70296
70297
70298             function mouseout() {
70299                 var service = getService();
70300                 if (service) { service.setStyles(context, null); }
70301             }
70302
70303
70304             function transform(d) {
70305                 var t = svgPointTransform(projection)(d);
70306                 if (d.pano && viewerCompassAngle !== null && isFinite(viewerCompassAngle)) {
70307                     t += ' rotate(' + Math.floor(viewerCompassAngle) + ',0,0)';
70308                 } else if (d.ca) {
70309                     t += ' rotate(' + Math.floor(d.ca) + ',0,0)';
70310                 }
70311                 return t;
70312             }
70313
70314             context.photos().on('change.mapillary_images', update);
70315
70316             function filterImages(images) {
70317                 var showsPano = context.photos().showsPanoramic();
70318                 var showsFlat = context.photos().showsFlat();
70319                 if (!showsPano || !showsFlat) {
70320                     images = images.filter(function(image) {
70321                         if (image.pano) { return showsPano; }
70322                         return showsFlat;
70323                     });
70324                 }
70325                 return images;
70326             }
70327
70328             function filterSequences(sequences, service) {
70329                 var showsPano = context.photos().showsPanoramic();
70330                 var showsFlat = context.photos().showsFlat();
70331                 if (!showsPano || !showsFlat) {
70332                     sequences = sequences.filter(function(sequence) {
70333                         if (sequence.properties.hasOwnProperty('pano')) {
70334                             if (sequence.properties.pano) { return showsPano; }
70335                             return showsFlat;
70336                         } else {
70337                             // if the sequence doesn't specify pano or not, search its images
70338                             var cProps = sequence.properties.coordinateProperties;
70339                             if (cProps && cProps.image_keys && cProps.image_keys.length > 0) {
70340                                 for (var index in cProps.image_keys) {
70341                                     var imageKey = cProps.image_keys[index];
70342                                     var image = service.cachedImage(imageKey);
70343                                     if (image && image.hasOwnProperty('pano')) {
70344                                         if (image.pano) { return showsPano; }
70345                                         return showsFlat;
70346                                     }
70347                                 }
70348                             }
70349                         }
70350                     });
70351                 }
70352                 return sequences;
70353             }
70354
70355             function update() {
70356
70357                 var z = ~~context.map().zoom();
70358                 var showMarkers = (z >= minMarkerZoom);
70359                 var showViewfields = (z >= minViewfieldZoom);
70360
70361                 var service = getService();
70362                 var selectedKey = service && service.getSelectedImageKey();
70363                 var sequences = (service ? service.sequences(projection) : []);
70364                 var images = (service && showMarkers ? service.images(projection) : []);
70365
70366                 images = filterImages(images);
70367                 sequences = filterSequences(sequences, service);
70368
70369                 var traces = layer.selectAll('.sequences').selectAll('.sequence')
70370                     .data(sequences, function(d) { return d.properties.key; });
70371
70372                 // exit
70373                 traces.exit()
70374                     .remove();
70375
70376                 // enter/update
70377                 traces = traces.enter()
70378                     .append('path')
70379                     .attr('class', 'sequence')
70380                     .merge(traces)
70381                     .attr('d', svgPath(projection).geojson);
70382
70383
70384                 var groups = layer.selectAll('.markers').selectAll('.viewfield-group')
70385                     .data(images, function(d) { return d.key; });
70386
70387                 // exit
70388                 groups.exit()
70389                     .remove();
70390
70391                 // enter
70392                 var groupsEnter = groups.enter()
70393                     .append('g')
70394                     .attr('class', 'viewfield-group')
70395                     .on('mouseenter', mouseover)
70396                     .on('mouseleave', mouseout)
70397                     .on('click', click);
70398
70399                 groupsEnter
70400                     .append('g')
70401                     .attr('class', 'viewfield-scale');
70402
70403                 // update
70404                 var markers = groups
70405                     .merge(groupsEnter)
70406                     .sort(function(a, b) {
70407                         return (a.key === selectedKey) ? 1
70408                             : (b.key === selectedKey) ? -1
70409                             : b.loc[1] - a.loc[1];  // sort Y
70410                     })
70411                     .attr('transform', transform)
70412                     .select('.viewfield-scale');
70413
70414
70415                 markers.selectAll('circle')
70416                     .data([0])
70417                     .enter()
70418                     .append('circle')
70419                     .attr('dx', '0')
70420                     .attr('dy', '0')
70421                     .attr('r', '6');
70422
70423                 var viewfields = markers.selectAll('.viewfield')
70424                     .data(showViewfields ? [0] : []);
70425
70426                 viewfields.exit()
70427                     .remove();
70428
70429                 viewfields.enter()               // viewfields may or may not be drawn...
70430                     .insert('path', 'circle')    // but if they are, draw below the circles
70431                     .attr('class', 'viewfield')
70432                     .classed('pano', function() { return this.parentNode.__data__.pano; })
70433                     .attr('transform', 'scale(1.5,1.5),translate(-8, -13)')
70434                     .attr('d', viewfieldPath);
70435
70436                 function viewfieldPath() {
70437                     var d = this.parentNode.__data__;
70438                     if (d.pano) {
70439                         return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0';
70440                     } else {
70441                         return 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z';
70442                     }
70443                 }
70444             }
70445
70446
70447             function drawImages(selection) {
70448                 var enabled = svgMapillaryImages.enabled;
70449                 var service = getService();
70450
70451                 layer = selection.selectAll('.layer-mapillary')
70452                     .data(service ? [0] : []);
70453
70454                 layer.exit()
70455                     .remove();
70456
70457                 var layerEnter = layer.enter()
70458                     .append('g')
70459                     .attr('class', 'layer-mapillary')
70460                     .style('display', enabled ? 'block' : 'none');
70461
70462                 layerEnter
70463                     .append('g')
70464                     .attr('class', 'sequences');
70465
70466                 layerEnter
70467                     .append('g')
70468                     .attr('class', 'markers');
70469
70470                 layer = layerEnter
70471                     .merge(layer);
70472
70473                 if (enabled) {
70474                     if (service && ~~context.map().zoom() >= minZoom) {
70475                         editOn();
70476                         update();
70477                         service.loadImages(projection);
70478                     } else {
70479                         editOff();
70480                     }
70481                 }
70482             }
70483
70484
70485             drawImages.enabled = function(_) {
70486                 if (!arguments.length) { return svgMapillaryImages.enabled; }
70487                 svgMapillaryImages.enabled = _;
70488                 if (svgMapillaryImages.enabled) {
70489                     showLayer();
70490                 } else {
70491                     hideLayer();
70492                 }
70493                 dispatch.call('change');
70494                 return this;
70495             };
70496
70497
70498             drawImages.supported = function() {
70499                 return !!getService();
70500             };
70501
70502
70503             init();
70504             return drawImages;
70505         }
70506
70507         function svgMapillarySigns(projection, context, dispatch) {
70508             var throttledRedraw = throttle(function () { dispatch.call('change'); }, 1000);
70509             var minZoom = 12;
70510             var layer = select(null);
70511             var _mapillary;
70512
70513
70514             function init() {
70515                 if (svgMapillarySigns.initialized) { return; }  // run once
70516                 svgMapillarySigns.enabled = false;
70517                 svgMapillarySigns.initialized = true;
70518             }
70519
70520
70521             function getService() {
70522                 if (services.mapillary && !_mapillary) {
70523                     _mapillary = services.mapillary;
70524                     _mapillary.event.on('loadedSigns', throttledRedraw);
70525                 } else if (!services.mapillary && _mapillary) {
70526                     _mapillary = null;
70527                 }
70528                 return _mapillary;
70529             }
70530
70531
70532             function showLayer() {
70533                 var service = getService();
70534                 if (!service) { return; }
70535
70536                 editOn();
70537             }
70538
70539
70540             function hideLayer() {
70541                 throttledRedraw.cancel();
70542                 editOff();
70543             }
70544
70545
70546             function editOn() {
70547                 layer.style('display', 'block');
70548             }
70549
70550
70551             function editOff() {
70552                 layer.selectAll('.icon-sign').remove();
70553                 layer.style('display', 'none');
70554             }
70555
70556
70557             function click(d) {
70558                 var service = getService();
70559                 if (!service) { return; }
70560
70561                 context.map().centerEase(d.loc);
70562
70563                 var selectedImageKey = service.getSelectedImageKey();
70564                 var imageKey;
70565
70566                 // Pick one of the images the sign was detected in,
70567                 // preference given to an image already selected.
70568                 d.detections.forEach(function(detection) {
70569                     if (!imageKey || selectedImageKey === detection.image_key) {
70570                         imageKey = detection.image_key;
70571                     }
70572                 });
70573
70574                 service
70575                     .selectImage(context, imageKey)
70576                     .updateViewer(context, imageKey)
70577                     .showViewer(context);
70578             }
70579
70580
70581             function update() {
70582                 var service = getService();
70583                 var data = (service ? service.signs(projection) : []);
70584                 var selectedImageKey = service.getSelectedImageKey();
70585                 var transform = svgPointTransform(projection);
70586
70587                 var signs = layer.selectAll('.icon-sign')
70588                     .data(data, function(d) { return d.key; });
70589
70590                 // exit
70591                 signs.exit()
70592                     .remove();
70593
70594                 // enter
70595                 var enter = signs.enter()
70596                     .append('g')
70597                     .attr('class', 'icon-sign icon-detected')
70598                     .on('click', click);
70599
70600                 enter
70601                     .append('use')
70602                     .attr('width', '24px')
70603                     .attr('height', '24px')
70604                     .attr('x', '-12px')
70605                     .attr('y', '-12px')
70606                     .attr('xlink:href', function(d) { return '#' + d.value; });
70607
70608                 enter
70609                     .append('rect')
70610                     .attr('width', '24px')
70611                     .attr('height', '24px')
70612                     .attr('x', '-12px')
70613                     .attr('y', '-12px');
70614
70615                 // update
70616                 signs
70617                     .merge(enter)
70618                     .attr('transform', transform)
70619                     .classed('currentView', function(d) {
70620                         return d.detections.some(function(detection) {
70621                             return detection.image_key === selectedImageKey;
70622                         });
70623                     })
70624                     .sort(function(a, b) {
70625                         var aSelected = a.detections.some(function(detection) {
70626                             return detection.image_key === selectedImageKey;
70627                         });
70628                         var bSelected = b.detections.some(function(detection) {
70629                             return detection.image_key === selectedImageKey;
70630                         });
70631                         if (aSelected === bSelected) {
70632                             return b.loc[1] - a.loc[1]; // sort Y
70633                         } else if (aSelected) {
70634                             return 1;
70635                         }
70636                         return -1;
70637                     });
70638             }
70639
70640
70641             function drawSigns(selection) {
70642                 var enabled = svgMapillarySigns.enabled;
70643                 var service = getService();
70644
70645                 layer = selection.selectAll('.layer-mapillary-signs')
70646                     .data(service ? [0] : []);
70647
70648                 layer.exit()
70649                     .remove();
70650
70651                 layer = layer.enter()
70652                     .append('g')
70653                     .attr('class', 'layer-mapillary-signs layer-mapillary-detections')
70654                     .style('display', enabled ? 'block' : 'none')
70655                     .merge(layer);
70656
70657                 if (enabled) {
70658                     if (service && ~~context.map().zoom() >= minZoom) {
70659                         editOn();
70660                         update();
70661                         service.loadSigns(projection);
70662                     } else {
70663                         editOff();
70664                     }
70665                 }
70666             }
70667
70668
70669             drawSigns.enabled = function(_) {
70670                 if (!arguments.length) { return svgMapillarySigns.enabled; }
70671                 svgMapillarySigns.enabled = _;
70672                 if (svgMapillarySigns.enabled) {
70673                     showLayer();
70674                 } else {
70675                     hideLayer();
70676                 }
70677                 dispatch.call('change');
70678                 return this;
70679             };
70680
70681
70682             drawSigns.supported = function() {
70683                 return !!getService();
70684             };
70685
70686
70687             init();
70688             return drawSigns;
70689         }
70690
70691         function svgMapillaryMapFeatures(projection, context, dispatch) {
70692             var throttledRedraw = throttle(function () { dispatch.call('change'); }, 1000);
70693             var minZoom = 12;
70694             var layer = select(null);
70695             var _mapillary;
70696
70697
70698             function init() {
70699                 if (svgMapillaryMapFeatures.initialized) { return; }  // run once
70700                 svgMapillaryMapFeatures.enabled = false;
70701                 svgMapillaryMapFeatures.initialized = true;
70702             }
70703
70704
70705             function getService() {
70706                 if (services.mapillary && !_mapillary) {
70707                     _mapillary = services.mapillary;
70708                     _mapillary.event.on('loadedMapFeatures', throttledRedraw);
70709                 } else if (!services.mapillary && _mapillary) {
70710                     _mapillary = null;
70711                 }
70712                 return _mapillary;
70713             }
70714
70715
70716             function showLayer() {
70717                 var service = getService();
70718                 if (!service) { return; }
70719
70720                 editOn();
70721             }
70722
70723
70724             function hideLayer() {
70725                 throttledRedraw.cancel();
70726                 editOff();
70727             }
70728
70729
70730             function editOn() {
70731                 layer.style('display', 'block');
70732             }
70733
70734
70735             function editOff() {
70736                 layer.selectAll('.icon-map-feature').remove();
70737                 layer.style('display', 'none');
70738             }
70739
70740
70741             function click(d) {
70742                 var service = getService();
70743                 if (!service) { return; }
70744
70745                 context.map().centerEase(d.loc);
70746
70747                 var selectedImageKey = service.getSelectedImageKey();
70748                 var imageKey;
70749
70750                 // Pick one of the images the map feature was detected in,
70751                 // preference given to an image already selected.
70752                 d.detections.forEach(function(detection) {
70753                     if (!imageKey || selectedImageKey === detection.image_key) {
70754                         imageKey = detection.image_key;
70755                     }
70756                 });
70757
70758                 service
70759                     .selectImage(context, imageKey)
70760                     .updateViewer(context, imageKey)
70761                     .showViewer(context);
70762             }
70763
70764
70765             function update() {
70766                 var service = getService();
70767                 var data = (service ? service.mapFeatures(projection) : []);
70768                 var selectedImageKey = service && service.getSelectedImageKey();
70769                 var transform = svgPointTransform(projection);
70770
70771                 var mapFeatures = layer.selectAll('.icon-map-feature')
70772                     .data(data, function(d) { return d.key; });
70773
70774                 // exit
70775                 mapFeatures.exit()
70776                     .remove();
70777
70778                 // enter
70779                 var enter = mapFeatures.enter()
70780                     .append('g')
70781                     .attr('class', 'icon-map-feature icon-detected')
70782                     .on('click', click);
70783
70784                 enter
70785                     .append('title')
70786                     .text(function(d) {
70787                         var id = d.value.replace(/--/g, '.').replace(/-/g, '_');
70788                         return _t('mapillary_map_features.' + id);
70789                     });
70790
70791                 enter
70792                     .append('use')
70793                     .attr('width', '24px')
70794                     .attr('height', '24px')
70795                     .attr('x', '-12px')
70796                     .attr('y', '-12px')
70797                     .attr('xlink:href', function(d) {
70798                         if (d.value === 'object--billboard') {
70799                             // no billboard icon right now, so use the advertisement icon
70800                             return '#object--sign--advertisement';
70801                         }
70802                         return '#' + d.value;
70803                     });
70804
70805                 enter
70806                     .append('rect')
70807                     .attr('width', '24px')
70808                     .attr('height', '24px')
70809                     .attr('x', '-12px')
70810                     .attr('y', '-12px');
70811
70812                 // update
70813                 mapFeatures
70814                     .merge(enter)
70815                     .attr('transform', transform)
70816                     .classed('currentView', function(d) {
70817                         return d.detections.some(function(detection) {
70818                             return detection.image_key === selectedImageKey;
70819                         });
70820                     })
70821                     .sort(function(a, b) {
70822                         var aSelected = a.detections.some(function(detection) {
70823                             return detection.image_key === selectedImageKey;
70824                         });
70825                         var bSelected = b.detections.some(function(detection) {
70826                             return detection.image_key === selectedImageKey;
70827                         });
70828                         if (aSelected === bSelected) {
70829                             return b.loc[1] - a.loc[1]; // sort Y
70830                         } else if (aSelected) {
70831                             return 1;
70832                         }
70833                         return -1;
70834                     });
70835             }
70836
70837
70838             function drawMapFeatures(selection) {
70839                 var enabled = svgMapillaryMapFeatures.enabled;
70840                 var service = getService();
70841
70842                 layer = selection.selectAll('.layer-mapillary-map-features')
70843                     .data(service ? [0] : []);
70844
70845                 layer.exit()
70846                     .remove();
70847
70848                 layer = layer.enter()
70849                     .append('g')
70850                     .attr('class', 'layer-mapillary-map-features layer-mapillary-detections')
70851                     .style('display', enabled ? 'block' : 'none')
70852                     .merge(layer);
70853
70854                 if (enabled) {
70855                     if (service && ~~context.map().zoom() >= minZoom) {
70856                         editOn();
70857                         update();
70858                         service.loadMapFeatures(projection);
70859                     } else {
70860                         editOff();
70861                     }
70862                 }
70863             }
70864
70865
70866             drawMapFeatures.enabled = function(_) {
70867                 if (!arguments.length) { return svgMapillaryMapFeatures.enabled; }
70868                 svgMapillaryMapFeatures.enabled = _;
70869                 if (svgMapillaryMapFeatures.enabled) {
70870                     showLayer();
70871                 } else {
70872                     hideLayer();
70873                 }
70874                 dispatch.call('change');
70875                 return this;
70876             };
70877
70878
70879             drawMapFeatures.supported = function() {
70880                 return !!getService();
70881             };
70882
70883
70884             init();
70885             return drawMapFeatures;
70886         }
70887
70888         function svgOpenstreetcamImages(projection, context, dispatch) {
70889             var throttledRedraw = throttle(function () { dispatch.call('change'); }, 1000);
70890             var minZoom = 12;
70891             var minMarkerZoom = 16;
70892             var minViewfieldZoom = 18;
70893             var layer = select(null);
70894             var _openstreetcam;
70895
70896
70897             function init() {
70898                 if (svgOpenstreetcamImages.initialized) { return; }  // run once
70899                 svgOpenstreetcamImages.enabled = false;
70900                 svgOpenstreetcamImages.initialized = true;
70901             }
70902
70903
70904             function getService() {
70905                 if (services.openstreetcam && !_openstreetcam) {
70906                     _openstreetcam = services.openstreetcam;
70907                     _openstreetcam.event.on('loadedImages', throttledRedraw);
70908                 } else if (!services.openstreetcam && _openstreetcam) {
70909                     _openstreetcam = null;
70910                 }
70911
70912                 return _openstreetcam;
70913             }
70914
70915
70916             function showLayer() {
70917                 var service = getService();
70918                 if (!service) { return; }
70919
70920                 editOn();
70921
70922                 layer
70923                     .style('opacity', 0)
70924                     .transition()
70925                     .duration(250)
70926                     .style('opacity', 1)
70927                     .on('end', function () { dispatch.call('change'); });
70928             }
70929
70930
70931             function hideLayer() {
70932                 throttledRedraw.cancel();
70933
70934                 layer
70935                     .transition()
70936                     .duration(250)
70937                     .style('opacity', 0)
70938                     .on('end', editOff);
70939             }
70940
70941
70942             function editOn() {
70943                 layer.style('display', 'block');
70944             }
70945
70946
70947             function editOff() {
70948                 layer.selectAll('.viewfield-group').remove();
70949                 layer.style('display', 'none');
70950             }
70951
70952
70953             function click(d) {
70954                 var service = getService();
70955                 if (!service) { return; }
70956
70957                 service
70958                     .selectImage(context, d)
70959                     .updateViewer(context, d)
70960                     .showViewer(context);
70961
70962                 context.map().centerEase(d.loc);
70963             }
70964
70965
70966             function mouseover(d) {
70967                 var service = getService();
70968                 if (service) { service.setStyles(context, d); }
70969             }
70970
70971
70972             function mouseout() {
70973                 var service = getService();
70974                 if (service) { service.setStyles(context, null); }
70975             }
70976
70977
70978             function transform(d) {
70979                 var t = svgPointTransform(projection)(d);
70980                 if (d.ca) {
70981                     t += ' rotate(' + Math.floor(d.ca) + ',0,0)';
70982                 }
70983                 return t;
70984             }
70985
70986
70987             context.photos().on('change.openstreetcam_images', update);
70988
70989             function update() {
70990                 var viewer = context.container().select('.photoviewer');
70991                 var selected = viewer.empty() ? undefined : viewer.datum();
70992
70993                 var z = ~~context.map().zoom();
70994                 var showMarkers = (z >= minMarkerZoom);
70995                 var showViewfields = (z >= minViewfieldZoom);
70996
70997                 var service = getService();
70998                 var sequences = [];
70999                 var images = [];
71000
71001                 if (context.photos().showsFlat()) {
71002                     sequences = (service ? service.sequences(projection) : []);
71003                     images = (service && showMarkers ? service.images(projection) : []);
71004                 }
71005
71006                 var traces = layer.selectAll('.sequences').selectAll('.sequence')
71007                     .data(sequences, function(d) { return d.properties.key; });
71008
71009                 // exit
71010                 traces.exit()
71011                     .remove();
71012
71013                 // enter/update
71014                 traces = traces.enter()
71015                     .append('path')
71016                     .attr('class', 'sequence')
71017                     .merge(traces)
71018                     .attr('d', svgPath(projection).geojson);
71019
71020
71021                 var groups = layer.selectAll('.markers').selectAll('.viewfield-group')
71022                     .data(images, function(d) { return d.key; });
71023
71024                 // exit
71025                 groups.exit()
71026                     .remove();
71027
71028                 // enter
71029                 var groupsEnter = groups.enter()
71030                     .append('g')
71031                     .attr('class', 'viewfield-group')
71032                     .on('mouseenter', mouseover)
71033                     .on('mouseleave', mouseout)
71034                     .on('click', click);
71035
71036                 groupsEnter
71037                     .append('g')
71038                     .attr('class', 'viewfield-scale');
71039
71040                 // update
71041                 var markers = groups
71042                     .merge(groupsEnter)
71043                     .sort(function(a, b) {
71044                         return (a === selected) ? 1
71045                             : (b === selected) ? -1
71046                             : b.loc[1] - a.loc[1];  // sort Y
71047                     })
71048                     .attr('transform', transform)
71049                     .select('.viewfield-scale');
71050
71051
71052                 markers.selectAll('circle')
71053                     .data([0])
71054                     .enter()
71055                     .append('circle')
71056                     .attr('dx', '0')
71057                     .attr('dy', '0')
71058                     .attr('r', '6');
71059
71060                 var viewfields = markers.selectAll('.viewfield')
71061                     .data(showViewfields ? [0] : []);
71062
71063                 viewfields.exit()
71064                     .remove();
71065
71066                 viewfields.enter()               // viewfields may or may not be drawn...
71067                     .insert('path', 'circle')    // but if they are, draw below the circles
71068                     .attr('class', 'viewfield')
71069                     .attr('transform', 'scale(1.5,1.5),translate(-8, -13)')
71070                     .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');
71071             }
71072
71073
71074             function drawImages(selection) {
71075                 var enabled = svgOpenstreetcamImages.enabled,
71076                     service = getService();
71077
71078                 layer = selection.selectAll('.layer-openstreetcam')
71079                     .data(service ? [0] : []);
71080
71081                 layer.exit()
71082                     .remove();
71083
71084                 var layerEnter = layer.enter()
71085                     .append('g')
71086                     .attr('class', 'layer-openstreetcam')
71087                     .style('display', enabled ? 'block' : 'none');
71088
71089                 layerEnter
71090                     .append('g')
71091                     .attr('class', 'sequences');
71092
71093                 layerEnter
71094                     .append('g')
71095                     .attr('class', 'markers');
71096
71097                 layer = layerEnter
71098                     .merge(layer);
71099
71100                 if (enabled) {
71101                     if (service && ~~context.map().zoom() >= minZoom) {
71102                         editOn();
71103                         update();
71104                         service.loadImages(projection);
71105                     } else {
71106                         editOff();
71107                     }
71108                 }
71109             }
71110
71111
71112             drawImages.enabled = function(_) {
71113                 if (!arguments.length) { return svgOpenstreetcamImages.enabled; }
71114                 svgOpenstreetcamImages.enabled = _;
71115                 if (svgOpenstreetcamImages.enabled) {
71116                     showLayer();
71117                 } else {
71118                     hideLayer();
71119                 }
71120                 dispatch.call('change');
71121                 return this;
71122             };
71123
71124
71125             drawImages.supported = function() {
71126                 return !!getService();
71127             };
71128
71129
71130             init();
71131             return drawImages;
71132         }
71133
71134         function svgOsm(projection, context, dispatch) {
71135             var enabled = true;
71136
71137
71138             function drawOsm(selection) {
71139                 selection.selectAll('.layer-osm')
71140                     .data(['covered', 'areas', 'lines', 'points', 'labels'])
71141                     .enter()
71142                     .append('g')
71143                     .attr('class', function(d) { return 'layer-osm ' + d; });
71144
71145                 selection.selectAll('.layer-osm.points').selectAll('.points-group')
71146                     .data(['points', 'midpoints', 'vertices', 'turns'])
71147                     .enter()
71148                     .append('g')
71149                     .attr('class', function(d) { return 'points-group ' + d; });
71150             }
71151
71152
71153             function showLayer() {
71154                 var layer = context.surface().selectAll('.data-layer.osm');
71155                 layer.interrupt();
71156
71157                 layer
71158                     .classed('disabled', false)
71159                     .style('opacity', 0)
71160                     .transition()
71161                     .duration(250)
71162                     .style('opacity', 1)
71163                     .on('end interrupt', function () {
71164                         dispatch.call('change');
71165                     });
71166             }
71167
71168
71169             function hideLayer() {
71170                 var layer = context.surface().selectAll('.data-layer.osm');
71171                 layer.interrupt();
71172
71173                 layer
71174                     .transition()
71175                     .duration(250)
71176                     .style('opacity', 0)
71177                     .on('end interrupt', function () {
71178                         layer.classed('disabled', true);
71179                         dispatch.call('change');
71180                     });
71181             }
71182
71183
71184             drawOsm.enabled = function(val) {
71185                 if (!arguments.length) { return enabled; }
71186                 enabled = val;
71187
71188                 if (enabled) {
71189                     showLayer();
71190                 } else {
71191                     hideLayer();
71192                 }
71193
71194                 dispatch.call('change');
71195                 return this;
71196             };
71197
71198
71199             return drawOsm;
71200         }
71201
71202         var _notesEnabled = false;
71203         var _osmService;
71204
71205
71206         function svgNotes(projection, context, dispatch$1) {
71207             if (!dispatch$1) { dispatch$1 = dispatch('change'); }
71208             var throttledRedraw = throttle(function () { dispatch$1.call('change'); }, 1000);
71209             var minZoom = 12;
71210             var touchLayer = select(null);
71211             var drawLayer = select(null);
71212             var _notesVisible = false;
71213
71214
71215             function markerPath(selection, klass) {
71216                 selection
71217                     .attr('class', klass)
71218                     .attr('transform', 'translate(-8, -22)')
71219                     .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');
71220             }
71221
71222
71223             // Loosely-coupled osm service for fetching notes.
71224             function getService() {
71225                 if (services.osm && !_osmService) {
71226                     _osmService = services.osm;
71227                     _osmService.on('loadedNotes', throttledRedraw);
71228                 } else if (!services.osm && _osmService) {
71229                     _osmService = null;
71230                 }
71231
71232                 return _osmService;
71233             }
71234
71235
71236             // Show the notes
71237             function editOn() {
71238                 if (!_notesVisible) {
71239                     _notesVisible = true;
71240                     drawLayer
71241                         .style('display', 'block');
71242                 }
71243             }
71244
71245
71246             // Immediately remove the notes and their touch targets
71247             function editOff() {
71248                 if (_notesVisible) {
71249                     _notesVisible = false;
71250                     drawLayer
71251                         .style('display', 'none');
71252                     drawLayer.selectAll('.note')
71253                         .remove();
71254                     touchLayer.selectAll('.note')
71255                         .remove();
71256                 }
71257             }
71258
71259
71260             // Enable the layer.  This shows the notes and transitions them to visible.
71261             function layerOn() {
71262                 editOn();
71263
71264                 drawLayer
71265                     .style('opacity', 0)
71266                     .transition()
71267                     .duration(250)
71268                     .style('opacity', 1)
71269                     .on('end interrupt', function () {
71270                         dispatch$1.call('change');
71271                     });
71272             }
71273
71274
71275             // Disable the layer.  This transitions the layer invisible and then hides the notes.
71276             function layerOff() {
71277                 throttledRedraw.cancel();
71278                 drawLayer.interrupt();
71279                 touchLayer.selectAll('.note')
71280                     .remove();
71281
71282                 drawLayer
71283                     .transition()
71284                     .duration(250)
71285                     .style('opacity', 0)
71286                     .on('end interrupt', function () {
71287                         editOff();
71288                         dispatch$1.call('change');
71289                     });
71290             }
71291
71292
71293             // Update the note markers
71294             function updateMarkers() {
71295                 if (!_notesVisible || !_notesEnabled) { return; }
71296
71297                 var service = getService();
71298                 var selectedID = context.selectedNoteID();
71299                 var data = (service ? service.notes(projection) : []);
71300                 var getTransform = svgPointTransform(projection);
71301
71302                 // Draw markers..
71303                 var notes = drawLayer.selectAll('.note')
71304                     .data(data, function(d) { return d.status + d.id; });
71305
71306                 // exit
71307                 notes.exit()
71308                     .remove();
71309
71310                 // enter
71311                 var notesEnter = notes.enter()
71312                     .append('g')
71313                     .attr('class', function(d) { return 'note note-' + d.id + ' ' + d.status; })
71314                     .classed('new', function(d) { return d.id < 0; });
71315
71316                 notesEnter
71317                     .append('ellipse')
71318                     .attr('cx', 0.5)
71319                     .attr('cy', 1)
71320                     .attr('rx', 6.5)
71321                     .attr('ry', 3)
71322                     .attr('class', 'stroke');
71323
71324                 notesEnter
71325                     .append('path')
71326                     .call(markerPath, 'shadow');
71327
71328                 notesEnter
71329                     .append('use')
71330                     .attr('class', 'note-fill')
71331                     .attr('width', '20px')
71332                     .attr('height', '20px')
71333                     .attr('x', '-8px')
71334                     .attr('y', '-22px')
71335                     .attr('xlink:href', '#iD-icon-note');
71336
71337                 notesEnter.selectAll('.icon-annotation')
71338                     .data(function(d) { return [d]; })
71339                     .enter()
71340                     .append('use')
71341                     .attr('class', 'icon-annotation')
71342                     .attr('width', '10px')
71343                     .attr('height', '10px')
71344                     .attr('x', '-3px')
71345                     .attr('y', '-19px')
71346                     .attr('xlink:href', function(d) {
71347                         return '#iD-icon-' + (d.id < 0 ? 'plus' : (d.status === 'open' ? 'close' : 'apply'));
71348                     });
71349
71350                 // update
71351                 notes
71352                     .merge(notesEnter)
71353                     .sort(sortY)
71354                     .classed('selected', function(d) {
71355                         var mode = context.mode();
71356                         var isMoving = mode && mode.id === 'drag-note';  // no shadows when dragging
71357                         return !isMoving && d.id === selectedID;
71358                     })
71359                     .attr('transform', getTransform);
71360
71361
71362                 // Draw targets..
71363                 if (touchLayer.empty()) { return; }
71364                 var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
71365
71366                 var targets = touchLayer.selectAll('.note')
71367                     .data(data, function(d) { return d.id; });
71368
71369                 // exit
71370                 targets.exit()
71371                     .remove();
71372
71373                 // enter/update
71374                 targets.enter()
71375                     .append('rect')
71376                     .attr('width', '20px')
71377                     .attr('height', '20px')
71378                     .attr('x', '-8px')
71379                     .attr('y', '-22px')
71380                     .merge(targets)
71381                     .sort(sortY)
71382                     .attr('class', function(d) {
71383                         var newClass = (d.id < 0 ? 'new' : '');
71384                         return 'note target note-' + d.id + ' ' + fillClass + newClass;
71385                     })
71386                     .attr('transform', getTransform);
71387
71388
71389                 function sortY(a, b) {
71390                     return (a.id === selectedID) ? 1 : (b.id === selectedID) ? -1 : b.loc[1] - a.loc[1];
71391                 }
71392             }
71393
71394
71395             // Draw the notes layer and schedule loading notes and updating markers.
71396             function drawNotes(selection) {
71397                 var service = getService();
71398
71399                 var surface = context.surface();
71400                 if (surface && !surface.empty()) {
71401                     touchLayer = surface.selectAll('.data-layer.touch .layer-touch.markers');
71402                 }
71403
71404                 drawLayer = selection.selectAll('.layer-notes')
71405                     .data(service ? [0] : []);
71406
71407                 drawLayer.exit()
71408                     .remove();
71409
71410                 drawLayer = drawLayer.enter()
71411                     .append('g')
71412                     .attr('class', 'layer-notes')
71413                     .style('display', _notesEnabled ? 'block' : 'none')
71414                     .merge(drawLayer);
71415
71416                 if (_notesEnabled) {
71417                     if (service && ~~context.map().zoom() >= minZoom) {
71418                         editOn();
71419                         service.loadNotes(projection);
71420                         updateMarkers();
71421                     } else {
71422                         editOff();
71423                     }
71424                 }
71425             }
71426
71427
71428             // Toggles the layer on and off
71429             drawNotes.enabled = function(val) {
71430                 if (!arguments.length) { return _notesEnabled; }
71431
71432                 _notesEnabled = val;
71433                 if (_notesEnabled) {
71434                     layerOn();
71435                 } else {
71436                     layerOff();
71437                     if (context.selectedNoteID()) {
71438                         context.enter(modeBrowse(context));
71439                     }
71440                 }
71441
71442                 dispatch$1.call('change');
71443                 return this;
71444             };
71445
71446
71447             return drawNotes;
71448         }
71449
71450         function svgTouch() {
71451
71452             function drawTouch(selection) {
71453                 selection.selectAll('.layer-touch')
71454                     .data(['areas', 'lines', 'points', 'turns', 'markers'])
71455                     .enter()
71456                     .append('g')
71457                     .attr('class', function(d) { return 'layer-touch ' + d; });
71458             }
71459
71460             return drawTouch;
71461         }
71462
71463         function refresh(selection, node) {
71464             var cr = node.getBoundingClientRect();
71465             var prop = [cr.width, cr.height];
71466             selection.property('__dimensions__', prop);
71467             return prop;
71468         }
71469
71470         function utilGetDimensions(selection, force) {
71471             if (!selection || selection.empty()) {
71472                 return [0, 0];
71473             }
71474             var node = selection.node(),
71475                 cached = selection.property('__dimensions__');
71476             return (!cached || force) ? refresh(selection, node) : cached;
71477         }
71478
71479
71480         function utilSetDimensions(selection, dimensions) {
71481             if (!selection || selection.empty()) {
71482                 return selection;
71483             }
71484             var node = selection.node();
71485             if (dimensions === null) {
71486                 refresh(selection, node);
71487                 return selection;
71488             }
71489             return selection
71490                 .property('__dimensions__', [dimensions[0], dimensions[1]])
71491                 .attr('width', dimensions[0])
71492                 .attr('height', dimensions[1]);
71493         }
71494
71495         function svgLayers(projection, context) {
71496             var dispatch$1 = dispatch('change');
71497             var svg = select(null);
71498             var _layers = [
71499                 { id: 'osm', layer: svgOsm(projection, context, dispatch$1) },
71500                 { id: 'notes', layer: svgNotes(projection, context, dispatch$1) },
71501                 { id: 'data', layer: svgData(projection, context, dispatch$1) },
71502                 { id: 'keepRight', layer: svgKeepRight(projection, context, dispatch$1) },
71503                 { id: 'improveOSM', layer: svgImproveOSM(projection, context, dispatch$1) },
71504                 { id: 'osmose', layer: svgOsmose(projection, context, dispatch$1) },
71505                 { id: 'streetside', layer: svgStreetside(projection, context, dispatch$1)},
71506                 { id: 'mapillary', layer: svgMapillaryImages(projection, context, dispatch$1) },
71507                 { id: 'mapillary-map-features',  layer: svgMapillaryMapFeatures(projection, context, dispatch$1) },
71508                 { id: 'mapillary-signs',  layer: svgMapillarySigns(projection, context, dispatch$1) },
71509                 { id: 'openstreetcam', layer: svgOpenstreetcamImages(projection, context, dispatch$1) },
71510                 { id: 'debug', layer: svgDebug(projection, context) },
71511                 { id: 'geolocate', layer: svgGeolocate(projection) },
71512                 { id: 'touch', layer: svgTouch() }
71513             ];
71514
71515
71516             function drawLayers(selection) {
71517                 svg = selection.selectAll('.surface')
71518                     .data([0]);
71519
71520                 svg = svg.enter()
71521                     .append('svg')
71522                     .attr('class', 'surface')
71523                     .merge(svg);
71524
71525                 var defs = svg.selectAll('.surface-defs')
71526                     .data([0]);
71527
71528                 defs.enter()
71529                     .append('defs')
71530                     .attr('class', 'surface-defs');
71531
71532                 var groups = svg.selectAll('.data-layer')
71533                     .data(_layers);
71534
71535                 groups.exit()
71536                     .remove();
71537
71538                 groups.enter()
71539                     .append('g')
71540                     .attr('class', function(d) { return 'data-layer ' + d.id; })
71541                     .merge(groups)
71542                     .each(function(d) { select(this).call(d.layer); });
71543             }
71544
71545
71546             drawLayers.all = function() {
71547                 return _layers;
71548             };
71549
71550
71551             drawLayers.layer = function(id) {
71552                 var obj = _layers.find(function(o) { return o.id === id; });
71553                 return obj && obj.layer;
71554             };
71555
71556
71557             drawLayers.only = function(what) {
71558                 var arr = [].concat(what);
71559                 var all = _layers.map(function(layer) { return layer.id; });
71560                 return drawLayers.remove(utilArrayDifference(all, arr));
71561             };
71562
71563
71564             drawLayers.remove = function(what) {
71565                 var arr = [].concat(what);
71566                 arr.forEach(function(id) {
71567                     _layers = _layers.filter(function(o) { return o.id !== id; });
71568                 });
71569                 dispatch$1.call('change');
71570                 return this;
71571             };
71572
71573
71574             drawLayers.add = function(what) {
71575                 var arr = [].concat(what);
71576                 arr.forEach(function(obj) {
71577                     if ('id' in obj && 'layer' in obj) {
71578                         _layers.push(obj);
71579                     }
71580                 });
71581                 dispatch$1.call('change');
71582                 return this;
71583             };
71584
71585
71586             drawLayers.dimensions = function(val) {
71587                 if (!arguments.length) { return utilGetDimensions(svg); }
71588                 utilSetDimensions(svg, val);
71589                 return this;
71590             };
71591
71592
71593             return utilRebind(drawLayers, dispatch$1, 'on');
71594         }
71595
71596         function svgLines(projection, context) {
71597             var detected = utilDetect();
71598
71599             var highway_stack = {
71600                 motorway: 0,
71601                 motorway_link: 1,
71602                 trunk: 2,
71603                 trunk_link: 3,
71604                 primary: 4,
71605                 primary_link: 5,
71606                 secondary: 6,
71607                 tertiary: 7,
71608                 unclassified: 8,
71609                 residential: 9,
71610                 service: 10,
71611                 footway: 11
71612             };
71613
71614
71615             function drawTargets(selection, graph, entities, filter) {
71616                 var targetClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
71617                 var nopeClass = context.getDebug('target') ? 'red ' : 'nocolor ';
71618                 var getPath = svgPath(projection).geojson;
71619                 var activeID = context.activeID();
71620                 var base = context.history().base();
71621
71622                 // The targets and nopes will be MultiLineString sub-segments of the ways
71623                 var data = { targets: [], nopes: [] };
71624
71625                 entities.forEach(function(way) {
71626                     var features = svgSegmentWay(way, graph, activeID);
71627                     data.targets.push.apply(data.targets, features.passive);
71628                     data.nopes.push.apply(data.nopes, features.active);
71629                 });
71630
71631
71632                 // Targets allow hover and vertex snapping
71633                 var targetData = data.targets.filter(getPath);
71634                 var targets = selection.selectAll('.line.target-allowed')
71635                     .filter(function(d) { return filter(d.properties.entity); })
71636                     .data(targetData, function key(d) { return d.id; });
71637
71638                 // exit
71639                 targets.exit()
71640                     .remove();
71641
71642                 var segmentWasEdited = function(d) {
71643                     var wayID = d.properties.entity.id;
71644                     // if the whole line was edited, don't draw segment changes
71645                     if (!base.entities[wayID] ||
71646                         !fastDeepEqual(graph.entities[wayID].nodes, base.entities[wayID].nodes)) {
71647                         return false;
71648                     }
71649                     return d.properties.nodes.some(function(n) {
71650                         return !base.entities[n.id] ||
71651                                !fastDeepEqual(graph.entities[n.id].loc, base.entities[n.id].loc);
71652                     });
71653                 };
71654
71655                 // enter/update
71656                 targets.enter()
71657                     .append('path')
71658                     .merge(targets)
71659                     .attr('d', getPath)
71660                     .attr('class', function(d) {
71661                         return 'way line target target-allowed ' + targetClass + d.id;
71662                     })
71663                     .classed('segment-edited', segmentWasEdited);
71664
71665                 // NOPE
71666                 var nopeData = data.nopes.filter(getPath);
71667                 var nopes = selection.selectAll('.line.target-nope')
71668                     .filter(function(d) { return filter(d.properties.entity); })
71669                     .data(nopeData, function key(d) { return d.id; });
71670
71671                 // exit
71672                 nopes.exit()
71673                     .remove();
71674
71675                 // enter/update
71676                 nopes.enter()
71677                     .append('path')
71678                     .merge(nopes)
71679                     .attr('d', getPath)
71680                     .attr('class', function(d) {
71681                         return 'way line target target-nope ' + nopeClass + d.id;
71682                     })
71683                     .classed('segment-edited', segmentWasEdited);
71684             }
71685
71686
71687             function drawLines(selection, graph, entities, filter) {
71688                 var base = context.history().base();
71689
71690                 function waystack(a, b) {
71691                     var selected = context.selectedIDs();
71692                     var scoreA = selected.indexOf(a.id) !== -1 ? 20 : 0;
71693                     var scoreB = selected.indexOf(b.id) !== -1 ? 20 : 0;
71694
71695                     if (a.tags.highway) { scoreA -= highway_stack[a.tags.highway]; }
71696                     if (b.tags.highway) { scoreB -= highway_stack[b.tags.highway]; }
71697                     return scoreA - scoreB;
71698                 }
71699
71700
71701                 function drawLineGroup(selection, klass, isSelected) {
71702                     // Note: Don't add `.selected` class in draw modes
71703                     var mode = context.mode();
71704                     var isDrawing = mode && /^draw/.test(mode.id);
71705                     var selectedClass = (!isDrawing && isSelected) ? 'selected ' : '';
71706
71707                     var lines = selection
71708                         .selectAll('path')
71709                         .filter(filter)
71710                         .data(getPathData(isSelected), osmEntity.key);
71711
71712                     lines.exit()
71713                         .remove();
71714
71715                     // Optimization: Call expensive TagClasses only on enter selection. This
71716                     // works because osmEntity.key is defined to include the entity v attribute.
71717                     lines.enter()
71718                         .append('path')
71719                         .attr('class', function(d) {
71720
71721                             var prefix = 'way line';
71722
71723                             // if this line isn't styled by its own tags
71724                             if (!d.hasInterestingTags()) {
71725
71726                                 var parentRelations = graph.parentRelations(d);
71727                                 var parentMultipolygons = parentRelations.filter(function(relation) {
71728                                     return relation.isMultipolygon();
71729                                 });
71730
71731                                 // and if it's a member of at least one multipolygon relation
71732                                 if (parentMultipolygons.length > 0 &&
71733                                     // and only multipolygon relations
71734                                     parentRelations.length === parentMultipolygons.length) {
71735                                     // then fudge the classes to style this as an area edge
71736                                     prefix = 'relation area';
71737                                 }
71738                             }
71739
71740                             var oldMPClass = oldMultiPolygonOuters[d.id] ? 'old-multipolygon ' : '';
71741                             return prefix + ' ' + klass + ' ' + selectedClass + oldMPClass + d.id;
71742                         })
71743                         .classed('added', function(d) {
71744                             return !base.entities[d.id];
71745                         })
71746                         .classed('geometry-edited', function(d) {
71747                             return graph.entities[d.id] &&
71748                                 base.entities[d.id] &&
71749                                 !fastDeepEqual(graph.entities[d.id].nodes, base.entities[d.id].nodes);
71750                         })
71751                         .classed('retagged', function(d) {
71752                             return graph.entities[d.id] &&
71753                                 base.entities[d.id] &&
71754                                 !fastDeepEqual(graph.entities[d.id].tags, base.entities[d.id].tags);
71755                         })
71756                         .call(svgTagClasses())
71757                         .merge(lines)
71758                         .sort(waystack)
71759                         .attr('d', getPath)
71760                         .call(svgTagClasses().tags(svgRelationMemberTags(graph)));
71761
71762                     return selection;
71763                 }
71764
71765
71766                 function getPathData(isSelected) {
71767                     return function() {
71768                         var layer = this.parentNode.__data__;
71769                         var data = pathdata[layer] || [];
71770                         return data.filter(function(d) {
71771                             if (isSelected)
71772                                 { return context.selectedIDs().indexOf(d.id) !== -1; }
71773                             else
71774                                 { return context.selectedIDs().indexOf(d.id) === -1; }
71775                         });
71776                     };
71777                 }
71778
71779                 function addMarkers(layergroup, pathclass, groupclass, groupdata, marker) {
71780                     var markergroup = layergroup
71781                         .selectAll('g.' + groupclass)
71782                         .data([pathclass]);
71783
71784                     markergroup = markergroup.enter()
71785                         .append('g')
71786                         .attr('class', groupclass)
71787                         .merge(markergroup);
71788
71789                     var markers = markergroup
71790                         .selectAll('path')
71791                         .filter(filter)
71792                         .data(
71793                             function data() { return groupdata[this.parentNode.__data__] || []; },
71794                             function key(d) { return [d.id, d.index]; }
71795                         );
71796
71797                     markers.exit()
71798                         .remove();
71799
71800                     markers = markers.enter()
71801                         .append('path')
71802                         .attr('class', pathclass)
71803                         .merge(markers)
71804                         .attr('marker-mid', marker)
71805                         .attr('d', function(d) { return d.d; });
71806
71807                     if (detected.ie) {
71808                         markers.each(function() { this.parentNode.insertBefore(this, this); });
71809                     }
71810                 }
71811
71812
71813                 var getPath = svgPath(projection, graph);
71814                 var ways = [];
71815                 var onewaydata = {};
71816                 var sideddata = {};
71817                 var oldMultiPolygonOuters = {};
71818
71819                 for (var i = 0; i < entities.length; i++) {
71820                     var entity = entities[i];
71821                     var outer = osmOldMultipolygonOuterMember(entity, graph);
71822                     if (outer) {
71823                         ways.push(entity.mergeTags(outer.tags));
71824                         oldMultiPolygonOuters[outer.id] = true;
71825                     } else if (entity.geometry(graph) === 'line') {
71826                         ways.push(entity);
71827                     }
71828                 }
71829
71830                 ways = ways.filter(getPath);
71831                 var pathdata = utilArrayGroupBy(ways, function(way) { return way.layer(); });
71832
71833                 Object.keys(pathdata).forEach(function(k) {
71834                     var v = pathdata[k];
71835                     var onewayArr = v.filter(function(d) { return d.isOneWay(); });
71836                     var onewaySegments = svgMarkerSegments(
71837                         projection, graph, 35,
71838                         function shouldReverse(entity) { return entity.tags.oneway === '-1'; },
71839                         function bothDirections(entity) {
71840                             return entity.tags.oneway === 'reversible' || entity.tags.oneway === 'alternating';
71841                         }
71842                     );
71843                     onewaydata[k] = utilArrayFlatten(onewayArr.map(onewaySegments));
71844
71845                     var sidedArr = v.filter(function(d) { return d.isSided(); });
71846                     var sidedSegments = svgMarkerSegments(
71847                         projection, graph, 30,
71848                         function shouldReverse() { return false; },
71849                         function bothDirections() { return false; }
71850                     );
71851                     sideddata[k] = utilArrayFlatten(sidedArr.map(sidedSegments));
71852                 });
71853
71854
71855                 var covered = selection.selectAll('.layer-osm.covered');     // under areas
71856                 var uncovered = selection.selectAll('.layer-osm.lines');     // over areas
71857                 var touchLayer = selection.selectAll('.layer-touch.lines');
71858
71859                 // Draw lines..
71860                 [covered, uncovered].forEach(function(selection) {
71861                     var range = (selection === covered ? range$1(-10,0) : range$1(0,11));
71862                     var layergroup = selection
71863                         .selectAll('g.layergroup')
71864                         .data(range);
71865
71866                     layergroup = layergroup.enter()
71867                         .append('g')
71868                         .attr('class', function(d) { return 'layergroup layer' + String(d); })
71869                         .merge(layergroup);
71870
71871                     layergroup
71872                         .selectAll('g.linegroup')
71873                         .data(['shadow', 'casing', 'stroke', 'shadow-highlighted', 'casing-highlighted', 'stroke-highlighted'])
71874                         .enter()
71875                         .append('g')
71876                         .attr('class', function(d) { return 'linegroup line-' + d; });
71877
71878                     layergroup.selectAll('g.line-shadow')
71879                         .call(drawLineGroup, 'shadow', false);
71880                     layergroup.selectAll('g.line-casing')
71881                         .call(drawLineGroup, 'casing', false);
71882                     layergroup.selectAll('g.line-stroke')
71883                         .call(drawLineGroup, 'stroke', false);
71884
71885                     layergroup.selectAll('g.line-shadow-highlighted')
71886                         .call(drawLineGroup, 'shadow', true);
71887                     layergroup.selectAll('g.line-casing-highlighted')
71888                         .call(drawLineGroup, 'casing', true);
71889                     layergroup.selectAll('g.line-stroke-highlighted')
71890                         .call(drawLineGroup, 'stroke', true);
71891
71892                     addMarkers(layergroup, 'oneway', 'onewaygroup', onewaydata, 'url(#ideditor-oneway-marker)');
71893                     addMarkers(layergroup, 'sided', 'sidedgroup', sideddata,
71894                         function marker(d) {
71895                             var category = graph.entity(d.id).sidednessIdentifier();
71896                             return 'url(#ideditor-sided-marker-' + category + ')';
71897                         }
71898                     );
71899                 });
71900
71901                 // Draw touch targets..
71902                 touchLayer
71903                     .call(drawTargets, graph, ways, filter);
71904             }
71905
71906
71907             return drawLines;
71908         }
71909
71910         function svgMidpoints(projection, context) {
71911             var targetRadius = 8;
71912
71913             function drawTargets(selection, graph, entities, filter) {
71914                 var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
71915                 var getTransform = svgPointTransform(projection).geojson;
71916
71917                 var data = entities.map(function(midpoint) {
71918                     return {
71919                         type: 'Feature',
71920                         id: midpoint.id,
71921                         properties: {
71922                             target: true,
71923                             entity: midpoint
71924                         },
71925                         geometry: {
71926                             type: 'Point',
71927                             coordinates: midpoint.loc
71928                         }
71929                     };
71930                 });
71931
71932                 var targets = selection.selectAll('.midpoint.target')
71933                     .filter(function(d) { return filter(d.properties.entity); })
71934                     .data(data, function key(d) { return d.id; });
71935
71936                 // exit
71937                 targets.exit()
71938                     .remove();
71939
71940                 // enter/update
71941                 targets.enter()
71942                     .append('circle')
71943                     .attr('r', targetRadius)
71944                     .merge(targets)
71945                     .attr('class', function(d) { return 'node midpoint target ' + fillClass + d.id; })
71946                     .attr('transform', getTransform);
71947             }
71948
71949
71950             function drawMidpoints(selection, graph, entities, filter, extent) {
71951                 var drawLayer = selection.selectAll('.layer-osm.points .points-group.midpoints');
71952                 var touchLayer = selection.selectAll('.layer-touch.points');
71953
71954                 var mode = context.mode();
71955                 if ((mode && mode.id !== 'select') || !context.map().withinEditableZoom()) {
71956                     drawLayer.selectAll('.midpoint').remove();
71957                     touchLayer.selectAll('.midpoint.target').remove();
71958                     return;
71959                 }
71960
71961                 var poly = extent.polygon();
71962                 var midpoints = {};
71963
71964                 for (var i = 0; i < entities.length; i++) {
71965                     var entity = entities[i];
71966
71967                     if (entity.type !== 'way') { continue; }
71968                     if (!filter(entity)) { continue; }
71969                     if (context.selectedIDs().indexOf(entity.id) < 0) { continue; }
71970
71971                     var nodes = graph.childNodes(entity);
71972                     for (var j = 0; j < nodes.length - 1; j++) {
71973                         var a = nodes[j];
71974                         var b = nodes[j + 1];
71975                         var id = [a.id, b.id].sort().join('-');
71976
71977                         if (midpoints[id]) {
71978                             midpoints[id].parents.push(entity);
71979                         } else if (geoVecLength(projection(a.loc), projection(b.loc)) > 40) {
71980                             var point = geoVecInterp(a.loc, b.loc, 0.5);
71981                             var loc = null;
71982
71983                             if (extent.intersects(point)) {
71984                                 loc = point;
71985                             } else {
71986                                 for (var k = 0; k < 4; k++) {
71987                                     point = geoLineIntersection([a.loc, b.loc], [poly[k], poly[k + 1]]);
71988                                     if (point &&
71989                                         geoVecLength(projection(a.loc), projection(point)) > 20 &&
71990                                         geoVecLength(projection(b.loc), projection(point)) > 20)
71991                                     {
71992                                         loc = point;
71993                                         break;
71994                                     }
71995                                 }
71996                             }
71997
71998                             if (loc) {
71999                                 midpoints[id] = {
72000                                     type: 'midpoint',
72001                                     id: id,
72002                                     loc: loc,
72003                                     edge: [a.id, b.id],
72004                                     parents: [entity]
72005                                 };
72006                             }
72007                         }
72008                     }
72009                 }
72010
72011
72012                 function midpointFilter(d) {
72013                     if (midpoints[d.id])
72014                         { return true; }
72015
72016                     for (var i = 0; i < d.parents.length; i++) {
72017                         if (filter(d.parents[i])) {
72018                             return true;
72019                         }
72020                     }
72021
72022                     return false;
72023                 }
72024
72025
72026                 var groups = drawLayer.selectAll('.midpoint')
72027                     .filter(midpointFilter)
72028                     .data(Object.values(midpoints), function(d) { return d.id; });
72029
72030                 groups.exit()
72031                     .remove();
72032
72033                 var enter = groups.enter()
72034                     .insert('g', ':first-child')
72035                     .attr('class', 'midpoint');
72036
72037                 enter
72038                     .append('polygon')
72039                     .attr('points', '-6,8 10,0 -6,-8')
72040                     .attr('class', 'shadow');
72041
72042                 enter
72043                     .append('polygon')
72044                     .attr('points', '-3,4 5,0 -3,-4')
72045                     .attr('class', 'fill');
72046
72047                 groups = groups
72048                     .merge(enter)
72049                     .attr('transform', function(d) {
72050                         var translate = svgPointTransform(projection);
72051                         var a = graph.entity(d.edge[0]);
72052                         var b = graph.entity(d.edge[1]);
72053                         var angle = geoAngle(a, b, projection) * (180 / Math.PI);
72054                         return translate(d) + ' rotate(' + angle + ')';
72055                     })
72056                     .call(svgTagClasses().tags(
72057                         function(d) { return d.parents[0].tags; }
72058                     ));
72059
72060                 // Propagate data bindings.
72061                 groups.select('polygon.shadow');
72062                 groups.select('polygon.fill');
72063
72064
72065                 // Draw touch targets..
72066                 touchLayer
72067                     .call(drawTargets, graph, Object.values(midpoints), midpointFilter);
72068             }
72069
72070             return drawMidpoints;
72071         }
72072
72073         function svgPoints(projection, context) {
72074
72075             function markerPath(selection, klass) {
72076                 selection
72077                     .attr('class', klass)
72078                     .attr('transform', 'translate(-8, -23)')
72079                     .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');
72080             }
72081
72082             function sortY(a, b) {
72083                 return b.loc[1] - a.loc[1];
72084             }
72085
72086
72087             // Avoid exit/enter if we're just moving stuff around.
72088             // The node will get a new version but we only need to run the update selection.
72089             function fastEntityKey(d) {
72090                 var mode = context.mode();
72091                 var isMoving = mode && /^(add|draw|drag|move|rotate)/.test(mode.id);
72092                 return isMoving ? d.id : osmEntity.key(d);
72093             }
72094
72095
72096             function drawTargets(selection, graph, entities, filter) {
72097                 var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
72098                 var getTransform = svgPointTransform(projection).geojson;
72099                 var activeID = context.activeID();
72100                 var data = [];
72101
72102                 entities.forEach(function(node) {
72103                     if (activeID === node.id) { return; }   // draw no target on the activeID
72104
72105                     data.push({
72106                         type: 'Feature',
72107                         id: node.id,
72108                         properties: {
72109                             target: true,
72110                             entity: node
72111                         },
72112                         geometry: node.asGeoJSON()
72113                     });
72114                 });
72115
72116                 var targets = selection.selectAll('.point.target')
72117                     .filter(function(d) { return filter(d.properties.entity); })
72118                     .data(data, function key(d) { return d.id; });
72119
72120                 // exit
72121                 targets.exit()
72122                     .remove();
72123
72124                 // enter/update
72125                 targets.enter()
72126                     .append('rect')
72127                     .attr('x', -10)
72128                     .attr('y', -26)
72129                     .attr('width', 20)
72130                     .attr('height', 30)
72131                     .merge(targets)
72132                     .attr('class', function(d) { return 'node point target ' + fillClass + d.id; })
72133                     .attr('transform', getTransform);
72134             }
72135
72136
72137             function drawPoints(selection, graph, entities, filter) {
72138                 var wireframe = context.surface().classed('fill-wireframe');
72139                 var zoom = geoScaleToZoom(projection.scale());
72140                 var base = context.history().base();
72141
72142                 // Points with a direction will render as vertices at higher zooms..
72143                 function renderAsPoint(entity) {
72144                     return entity.geometry(graph) === 'point' &&
72145                         !(zoom >= 18 && entity.directions(graph, projection).length);
72146                 }
72147
72148                 // All points will render as vertices in wireframe mode too..
72149                 var points = wireframe ? [] : entities.filter(renderAsPoint);
72150                 points.sort(sortY);
72151
72152
72153                 var drawLayer = selection.selectAll('.layer-osm.points .points-group.points');
72154                 var touchLayer = selection.selectAll('.layer-touch.points');
72155
72156                 // Draw points..
72157                 var groups = drawLayer.selectAll('g.point')
72158                     .filter(filter)
72159                     .data(points, fastEntityKey);
72160
72161                 groups.exit()
72162                     .remove();
72163
72164                 var enter = groups.enter()
72165                     .append('g')
72166                     .attr('class', function(d) { return 'node point ' + d.id; })
72167                     .order();
72168
72169                 enter
72170                     .append('path')
72171                     .call(markerPath, 'shadow');
72172
72173                 enter
72174                     .append('ellipse')
72175                     .attr('cx', 0.5)
72176                     .attr('cy', 1)
72177                     .attr('rx', 6.5)
72178                     .attr('ry', 3)
72179                     .attr('class', 'stroke');
72180
72181                 enter
72182                     .append('path')
72183                     .call(markerPath, 'stroke');
72184
72185                 enter
72186                     .append('use')
72187                     .attr('transform', 'translate(-5, -19)')
72188                     .attr('class', 'icon')
72189                     .attr('width', '11px')
72190                     .attr('height', '11px');
72191
72192                 groups = groups
72193                     .merge(enter)
72194                     .attr('transform', svgPointTransform(projection))
72195                     .classed('added', function(d) {
72196                         return !base.entities[d.id]; // if it doesn't exist in the base graph, it's new
72197                     })
72198                     .classed('moved', function(d) {
72199                         return base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].loc, base.entities[d.id].loc);
72200                     })
72201                     .classed('retagged', function(d) {
72202                         return base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].tags, base.entities[d.id].tags);
72203                     })
72204                     .call(svgTagClasses());
72205
72206                 groups.select('.shadow');   // propagate bound data
72207                 groups.select('.stroke');   // propagate bound data
72208                 groups.select('.icon')      // propagate bound data
72209                     .attr('xlink:href', function(entity) {
72210                         var preset = _mainPresetIndex.match(entity, graph);
72211                         var picon = preset && preset.icon;
72212
72213                         if (!picon) {
72214                             return '';
72215                         } else {
72216                             var isMaki = /^maki-/.test(picon);
72217                             return '#' + picon + (isMaki ? '-11' : '');
72218                         }
72219                     });
72220
72221
72222                 // Draw touch targets..
72223                 touchLayer
72224                     .call(drawTargets, graph, points, filter);
72225             }
72226
72227
72228             return drawPoints;
72229         }
72230
72231         function svgTurns(projection, context) {
72232
72233             function icon(turn) {
72234                 var u = turn.u ? '-u' : '';
72235                 if (turn.no) { return '#iD-turn-no' + u; }
72236                 if (turn.only) { return '#iD-turn-only' + u; }
72237                 return '#iD-turn-yes' + u;
72238             }
72239
72240             function drawTurns(selection, graph, turns) {
72241
72242                 function turnTransform(d) {
72243                     var pxRadius = 50;
72244                     var toWay = graph.entity(d.to.way);
72245                     var toPoints = graph.childNodes(toWay)
72246                         .map(function (n) { return n.loc; })
72247                         .map(projection);
72248                     var toLength = geoPathLength(toPoints);
72249                     var mid = toLength / 2;    // midpoint of destination way
72250
72251                     var toNode = graph.entity(d.to.node);
72252                     var toVertex = graph.entity(d.to.vertex);
72253                     var a = geoAngle(toVertex, toNode, projection);
72254                     var o = projection(toVertex.loc);
72255                     var r = d.u ? 0                  // u-turn: no radius
72256                         : !toWay.__via ? pxRadius    // leaf way: put marker at pxRadius
72257                         : Math.min(mid, pxRadius);   // via way: prefer pxRadius, fallback to mid for very short ways
72258
72259                     return 'translate(' + (r * Math.cos(a) + o[0]) + ',' + (r * Math.sin(a) + o[1]) + ') ' +
72260                         'rotate(' + a * 180 / Math.PI + ')';
72261                 }
72262
72263
72264                 var drawLayer = selection.selectAll('.layer-osm.points .points-group.turns');
72265                 var touchLayer = selection.selectAll('.layer-touch.turns');
72266
72267                 // Draw turns..
72268                 var groups = drawLayer.selectAll('g.turn')
72269                     .data(turns, function(d) { return d.key; });
72270
72271                 // exit
72272                 groups.exit()
72273                     .remove();
72274
72275                 // enter
72276                 var groupsEnter = groups.enter()
72277                     .append('g')
72278                     .attr('class', function(d) { return 'turn ' + d.key; });
72279
72280                 var turnsEnter = groupsEnter
72281                     .filter(function(d) { return !d.u; });
72282
72283                 turnsEnter.append('rect')
72284                     .attr('transform', 'translate(-22, -12)')
72285                     .attr('width', '44')
72286                     .attr('height', '24');
72287
72288                 turnsEnter.append('use')
72289                     .attr('transform', 'translate(-22, -12)')
72290                     .attr('width', '44')
72291                     .attr('height', '24');
72292
72293                 var uEnter = groupsEnter
72294                     .filter(function(d) { return d.u; });
72295
72296                 uEnter.append('circle')
72297                     .attr('r', '16');
72298
72299                 uEnter.append('use')
72300                     .attr('transform', 'translate(-16, -16)')
72301                     .attr('width', '32')
72302                     .attr('height', '32');
72303
72304                 // update
72305                 groups = groups
72306                     .merge(groupsEnter)
72307                     .attr('opacity', function(d) { return d.direct === false ? '0.7' : null; })
72308                     .attr('transform', turnTransform);
72309
72310                 groups.select('use')
72311                     .attr('xlink:href', icon);
72312
72313                 groups.select('rect');      // propagate bound data
72314                 groups.select('circle');    // propagate bound data
72315
72316
72317                 // Draw touch targets..
72318                 var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
72319                 groups = touchLayer.selectAll('g.turn')
72320                     .data(turns, function(d) { return d.key; });
72321
72322                 // exit
72323                 groups.exit()
72324                     .remove();
72325
72326                 // enter
72327                 groupsEnter = groups.enter()
72328                     .append('g')
72329                     .attr('class', function(d) { return 'turn ' + d.key; });
72330
72331                 turnsEnter = groupsEnter
72332                     .filter(function(d) { return !d.u; });
72333
72334                 turnsEnter.append('rect')
72335                     .attr('class', 'target ' + fillClass)
72336                     .attr('transform', 'translate(-22, -12)')
72337                     .attr('width', '44')
72338                     .attr('height', '24');
72339
72340                 uEnter = groupsEnter
72341                     .filter(function(d) { return d.u; });
72342
72343                 uEnter.append('circle')
72344                     .attr('class', 'target ' + fillClass)
72345                     .attr('r', '16');
72346
72347                 // update
72348                 groups = groups
72349                     .merge(groupsEnter)
72350                     .attr('transform', turnTransform);
72351
72352                 groups.select('rect');      // propagate bound data
72353                 groups.select('circle');    // propagate bound data
72354
72355
72356                 return this;
72357             }
72358
72359             return drawTurns;
72360         }
72361
72362         function svgVertices(projection, context) {
72363             var radiuses = {
72364                 //       z16-, z17,   z18+,  w/icon
72365                 shadow: [6,    7.5,   7.5,   12],
72366                 stroke: [2.5,  3.5,   3.5,   8],
72367                 fill:   [1,    1.5,   1.5,   1.5]
72368             };
72369
72370             var _currHoverTarget;
72371             var _currPersistent = {};
72372             var _currHover = {};
72373             var _prevHover = {};
72374             var _currSelected = {};
72375             var _prevSelected = {};
72376             var _radii = {};
72377
72378
72379             function sortY(a, b) {
72380                 return b.loc[1] - a.loc[1];
72381             }
72382
72383             // Avoid exit/enter if we're just moving stuff around.
72384             // The node will get a new version but we only need to run the update selection.
72385             function fastEntityKey(d) {
72386                 var mode = context.mode();
72387                 var isMoving = mode && /^(add|draw|drag|move|rotate)/.test(mode.id);
72388                 return isMoving ? d.id : osmEntity.key(d);
72389             }
72390
72391
72392             function draw(selection, graph, vertices, sets, filter) {
72393                 sets = sets || { selected: {}, important: {}, hovered: {} };
72394
72395                 var icons = {};
72396                 var directions = {};
72397                 var wireframe = context.surface().classed('fill-wireframe');
72398                 var zoom = geoScaleToZoom(projection.scale());
72399                 var z = (zoom < 17 ? 0 : zoom < 18 ? 1 : 2);
72400                 var activeID = context.activeID();
72401                 var base = context.history().base();
72402
72403
72404                 function getIcon(d) {
72405                     // always check latest entity, as fastEntityKey avoids enter/exit now
72406                     var entity = graph.entity(d.id);
72407                     if (entity.id in icons) { return icons[entity.id]; }
72408
72409                     icons[entity.id] =
72410                         entity.hasInterestingTags() &&
72411                         _mainPresetIndex.match(entity, graph).icon;
72412
72413                     return icons[entity.id];
72414                 }
72415
72416
72417                 // memoize directions results, return false for empty arrays (for use in filter)
72418                 function getDirections(entity) {
72419                     if (entity.id in directions) { return directions[entity.id]; }
72420
72421                     var angles = entity.directions(graph, projection);
72422                     directions[entity.id] = angles.length ? angles : false;
72423                     return angles;
72424                 }
72425
72426
72427                 function updateAttributes(selection) {
72428                     ['shadow', 'stroke', 'fill'].forEach(function(klass) {
72429                         var rads = radiuses[klass];
72430                         selection.selectAll('.' + klass)
72431                             .each(function(entity) {
72432                                 var i = z && getIcon(entity);
72433                                 var r = rads[i ? 3 : z];
72434
72435                                 // slightly increase the size of unconnected endpoints #3775
72436                                 if (entity.id !== activeID && entity.isEndpoint(graph) && !entity.isConnected(graph)) {
72437                                     r += 1.5;
72438                                 }
72439
72440                                 if (klass === 'shadow') {   // remember this value, so we don't need to
72441                                     _radii[entity.id] = r;  // recompute it when we draw the touch targets
72442                                 }
72443
72444                                 select(this)
72445                                     .attr('r', r)
72446                                     .attr('visibility', (i && klass === 'fill') ? 'hidden' : null);
72447                             });
72448                     });
72449                 }
72450
72451                 vertices.sort(sortY);
72452
72453                 var groups = selection.selectAll('g.vertex')
72454                     .filter(filter)
72455                     .data(vertices, fastEntityKey);
72456
72457                 // exit
72458                 groups.exit()
72459                     .remove();
72460
72461                 // enter
72462                 var enter = groups.enter()
72463                     .append('g')
72464                     .attr('class', function(d) { return 'node vertex ' + d.id; })
72465                     .order();
72466
72467                 enter
72468                     .append('circle')
72469                     .attr('class', 'shadow');
72470
72471                 enter
72472                     .append('circle')
72473                     .attr('class', 'stroke');
72474
72475                 // Vertices with tags get a fill.
72476                 enter.filter(function(d) { return d.hasInterestingTags(); })
72477                     .append('circle')
72478                     .attr('class', 'fill');
72479
72480                 // update
72481                 groups = groups
72482                     .merge(enter)
72483                     .attr('transform', svgPointTransform(projection))
72484                     .classed('sibling', function(d) { return d.id in sets.selected; })
72485                     .classed('shared', function(d) { return graph.isShared(d); })
72486                     .classed('endpoint', function(d) { return d.isEndpoint(graph); })
72487                     .classed('added', function(d) {
72488                         return !base.entities[d.id]; // if it doesn't exist in the base graph, it's new
72489                     })
72490                     .classed('moved', function(d) {
72491                         return base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].loc, base.entities[d.id].loc);
72492                     })
72493                     .classed('retagged', function(d) {
72494                         return base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].tags, base.entities[d.id].tags);
72495                     })
72496                     .call(updateAttributes);
72497
72498                 // Vertices with icons get a `use`.
72499                 var iconUse = groups
72500                     .selectAll('.icon')
72501                     .data(function data(d) { return zoom >= 17 && getIcon(d) ? [d] : []; }, fastEntityKey);
72502
72503                 // exit
72504                 iconUse.exit()
72505                     .remove();
72506
72507                 // enter
72508                 iconUse.enter()
72509                     .append('use')
72510                     .attr('class', 'icon')
72511                     .attr('width', '11px')
72512                     .attr('height', '11px')
72513                     .attr('transform', 'translate(-5.5, -5.5)')
72514                     .attr('xlink:href', function(d) {
72515                         var picon = getIcon(d);
72516                         var isMaki = /^maki-/.test(picon);
72517                         return '#' + picon + (isMaki ? '-11' : '');
72518                     });
72519
72520
72521                 // Vertices with directions get viewfields
72522                 var dgroups = groups
72523                     .selectAll('.viewfieldgroup')
72524                     .data(function data(d) { return zoom >= 18 && getDirections(d) ? [d] : []; }, fastEntityKey);
72525
72526                 // exit
72527                 dgroups.exit()
72528                     .remove();
72529
72530                 // enter/update
72531                 dgroups = dgroups.enter()
72532                     .insert('g', '.shadow')
72533                     .attr('class', 'viewfieldgroup')
72534                     .merge(dgroups);
72535
72536                 var viewfields = dgroups.selectAll('.viewfield')
72537                     .data(getDirections, function key(d) { return osmEntity.key(d); });
72538
72539                 // exit
72540                 viewfields.exit()
72541                     .remove();
72542
72543                 // enter/update
72544                 viewfields.enter()
72545                     .append('path')
72546                     .attr('class', 'viewfield')
72547                     .attr('d', 'M0,0H0')
72548                     .merge(viewfields)
72549                     .attr('marker-start', 'url(#ideditor-viewfield-marker' + (wireframe ? '-wireframe' : '') + ')')
72550                     .attr('transform', function(d) { return 'rotate(' + d + ')'; });
72551             }
72552
72553
72554             function drawTargets(selection, graph, entities, filter) {
72555                 var targetClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
72556                 var nopeClass = context.getDebug('target') ? 'red ' : 'nocolor ';
72557                 var getTransform = svgPointTransform(projection).geojson;
72558                 var activeID = context.activeID();
72559                 var data = { targets: [], nopes: [] };
72560
72561                 entities.forEach(function(node) {
72562                     if (activeID === node.id) { return; }   // draw no target on the activeID
72563
72564                     var vertexType = svgPassiveVertex(node, graph, activeID);
72565                     if (vertexType !== 0) {     // passive or adjacent - allow to connect
72566                         data.targets.push({
72567                             type: 'Feature',
72568                             id: node.id,
72569                             properties: {
72570                                 target: true,
72571                                 entity: node
72572                             },
72573                             geometry: node.asGeoJSON()
72574                         });
72575                     } else {
72576                         data.nopes.push({
72577                             type: 'Feature',
72578                             id: node.id + '-nope',
72579                             properties: {
72580                                 nope: true,
72581                                 target: true,
72582                                 entity: node
72583                             },
72584                             geometry: node.asGeoJSON()
72585                         });
72586                     }
72587                 });
72588
72589                 // Targets allow hover and vertex snapping
72590                 var targets = selection.selectAll('.vertex.target-allowed')
72591                     .filter(function(d) { return filter(d.properties.entity); })
72592                     .data(data.targets, function key(d) { return d.id; });
72593
72594                 // exit
72595                 targets.exit()
72596                     .remove();
72597
72598                 // enter/update
72599                 targets.enter()
72600                     .append('circle')
72601                     .attr('r', function(d) {
72602                         return _radii[d.id]
72603                           || radiuses.shadow[3];
72604                     })
72605                     .merge(targets)
72606                     .attr('class', function(d) {
72607                         return 'node vertex target target-allowed '
72608                         + targetClass + d.id;
72609                     })
72610                     .attr('transform', getTransform);
72611
72612
72613                 // NOPE
72614                 var nopes = selection.selectAll('.vertex.target-nope')
72615                     .filter(function(d) { return filter(d.properties.entity); })
72616                     .data(data.nopes, function key(d) { return d.id; });
72617
72618                 // exit
72619                 nopes.exit()
72620                     .remove();
72621
72622                 // enter/update
72623                 nopes.enter()
72624                     .append('circle')
72625                     .attr('r', function(d) { return (_radii[d.properties.entity.id] || radiuses.shadow[3]); })
72626                     .merge(nopes)
72627                     .attr('class', function(d) { return 'node vertex target target-nope ' + nopeClass + d.id; })
72628                     .attr('transform', getTransform);
72629             }
72630
72631
72632             // Points can also render as vertices:
72633             // 1. in wireframe mode or
72634             // 2. at higher zooms if they have a direction
72635             function renderAsVertex(entity, graph, wireframe, zoom) {
72636                 var geometry = entity.geometry(graph);
72637                 return geometry === 'vertex' || (geometry === 'point' && (
72638                     wireframe || (zoom >= 18 && entity.directions(graph, projection).length)
72639                 ));
72640             }
72641
72642
72643             function isEditedNode(node, base, head) {
72644                 var baseNode = base.entities[node.id];
72645                 var headNode = head.entities[node.id];
72646                 return !headNode ||
72647                     !baseNode ||
72648                     !fastDeepEqual(headNode.tags, baseNode.tags) ||
72649                     !fastDeepEqual(headNode.loc, baseNode.loc);
72650             }
72651
72652
72653             function getSiblingAndChildVertices(ids, graph, wireframe, zoom) {
72654                 var results = {};
72655
72656                 var seenIds = {};
72657
72658                 function addChildVertices(entity) {
72659
72660                     // avoid redunant work and infinite recursion of circular relations
72661                     if (seenIds[entity.id]) { return; }
72662                     seenIds[entity.id] = true;
72663
72664                     var geometry = entity.geometry(graph);
72665                     if (!context.features().isHiddenFeature(entity, graph, geometry)) {
72666                         var i;
72667                         if (entity.type === 'way') {
72668                             for (i = 0; i < entity.nodes.length; i++) {
72669                                 var child = graph.hasEntity(entity.nodes[i]);
72670                                 if (child) {
72671                                     addChildVertices(child);
72672                                 }
72673                             }
72674                         } else if (entity.type === 'relation') {
72675                             for (i = 0; i < entity.members.length; i++) {
72676                                 var member = graph.hasEntity(entity.members[i].id);
72677                                 if (member) {
72678                                     addChildVertices(member);
72679                                 }
72680                             }
72681                         } else if (renderAsVertex(entity, graph, wireframe, zoom)) {
72682                             results[entity.id] = entity;
72683                         }
72684                     }
72685                 }
72686
72687                 ids.forEach(function(id) {
72688                     var entity = graph.hasEntity(id);
72689                     if (!entity) { return; }
72690
72691                     if (entity.type === 'node') {
72692                         if (renderAsVertex(entity, graph, wireframe, zoom)) {
72693                             results[entity.id] = entity;
72694                             graph.parentWays(entity).forEach(function(entity) {
72695                                 addChildVertices(entity);
72696                             });
72697                         }
72698                     } else {  // way, relation
72699                         addChildVertices(entity);
72700                     }
72701                 });
72702
72703                 return results;
72704             }
72705
72706
72707             function drawVertices(selection, graph, entities, filter, extent, fullRedraw) {
72708                 var wireframe = context.surface().classed('fill-wireframe');
72709                 var visualDiff = context.surface().classed('highlight-edited');
72710                 var zoom = geoScaleToZoom(projection.scale());
72711                 var mode = context.mode();
72712                 var isMoving = mode && /^(add|draw|drag|move|rotate)/.test(mode.id);
72713                 var base = context.history().base();
72714
72715                 var drawLayer = selection.selectAll('.layer-osm.points .points-group.vertices');
72716                 var touchLayer = selection.selectAll('.layer-touch.points');
72717
72718                 if (fullRedraw) {
72719                     _currPersistent = {};
72720                     _radii = {};
72721                 }
72722
72723                 // Collect important vertices from the `entities` list..
72724                 // (during a paritial redraw, it will not contain everything)
72725                 for (var i = 0; i < entities.length; i++) {
72726                     var entity = entities[i];
72727                     var geometry = entity.geometry(graph);
72728                     var keep = false;
72729
72730                     // a point that looks like a vertex..
72731                     if ((geometry === 'point') && renderAsVertex(entity, graph, wireframe, zoom)) {
72732                         _currPersistent[entity.id] = entity;
72733                         keep = true;
72734
72735                     // a vertex of some importance..
72736                     } else if (geometry === 'vertex' &&
72737                         (entity.hasInterestingTags() || entity.isEndpoint(graph) || entity.isConnected(graph)
72738                         || (visualDiff && isEditedNode(entity, base, graph)))) {
72739                         _currPersistent[entity.id] = entity;
72740                         keep = true;
72741                     }
72742
72743                     // whatever this is, it's not a persistent vertex..
72744                     if (!keep && !fullRedraw) {
72745                         delete _currPersistent[entity.id];
72746                     }
72747                 }
72748
72749                 // 3 sets of vertices to consider:
72750                 var sets = {
72751                     persistent: _currPersistent,  // persistent = important vertices (render always)
72752                     selected: _currSelected,      // selected + siblings of selected (render always)
72753                     hovered: _currHover           // hovered + siblings of hovered (render only in draw modes)
72754                 };
72755
72756                 var all = Object.assign({}, (isMoving ? _currHover : {}), _currSelected, _currPersistent);
72757
72758                 // Draw the vertices..
72759                 // The filter function controls the scope of what objects d3 will touch (exit/enter/update)
72760                 // Adjust the filter function to expand the scope beyond whatever entities were passed in.
72761                 var filterRendered = function(d) {
72762                     return d.id in _currPersistent || d.id in _currSelected || d.id in _currHover || filter(d);
72763                 };
72764                 drawLayer
72765                     .call(draw, graph, currentVisible(all), sets, filterRendered);
72766
72767                 // Draw touch targets..
72768                 // When drawing, render all targets (not just those affected by a partial redraw)
72769                 var filterTouch = function(d) {
72770                     return isMoving ? true : filterRendered(d);
72771                 };
72772                 touchLayer
72773                     .call(drawTargets, graph, currentVisible(all), filterTouch);
72774
72775
72776                 function currentVisible(which) {
72777                     return Object.keys(which)
72778                         .map(graph.hasEntity, graph)     // the current version of this entity
72779                         .filter(function (entity) { return entity && entity.intersects(extent, graph); });
72780                 }
72781             }
72782
72783
72784             // partial redraw - only update the selected items..
72785             drawVertices.drawSelected = function(selection, graph, extent) {
72786                 var wireframe = context.surface().classed('fill-wireframe');
72787                 var zoom = geoScaleToZoom(projection.scale());
72788
72789                 _prevSelected = _currSelected || {};
72790                 if (context.map().isInWideSelection()) {
72791                     _currSelected = {};
72792                     context.selectedIDs().forEach(function(id) {
72793                         var entity = graph.hasEntity(id);
72794                         if (!entity) { return; }
72795
72796                         if (entity.type === 'node') {
72797                             if (renderAsVertex(entity, graph, wireframe, zoom)) {
72798                                 _currSelected[entity.id] = entity;
72799                             }
72800                         }
72801                     });
72802
72803                 } else {
72804                     _currSelected = getSiblingAndChildVertices(context.selectedIDs(), graph, wireframe, zoom);
72805                 }
72806
72807                 // note that drawVertices will add `_currSelected` automatically if needed..
72808                 var filter = function(d) { return d.id in _prevSelected; };
72809                 drawVertices(selection, graph, Object.values(_prevSelected), filter, extent, false);
72810             };
72811
72812
72813             // partial redraw - only update the hovered items..
72814             drawVertices.drawHover = function(selection, graph, target, extent) {
72815                 if (target === _currHoverTarget) { return; }  // continue only if something changed
72816
72817                 var wireframe = context.surface().classed('fill-wireframe');
72818                 var zoom = geoScaleToZoom(projection.scale());
72819
72820                 _prevHover = _currHover || {};
72821                 _currHoverTarget = target;
72822                 var entity = target && target.properties && target.properties.entity;
72823
72824                 if (entity) {
72825                     _currHover = getSiblingAndChildVertices([entity.id], graph, wireframe, zoom);
72826                 } else {
72827                     _currHover = {};
72828                 }
72829
72830                 // note that drawVertices will add `_currHover` automatically if needed..
72831                 var filter = function(d) { return d.id in _prevHover; };
72832                 drawVertices(selection, graph, Object.values(_prevHover), filter, extent, false);
72833             };
72834
72835             return drawVertices;
72836         }
72837
72838         function utilBindOnce(target, type, listener, capture) {
72839             var typeOnce = type + '.once';
72840             function one() {
72841                 target.on(typeOnce, null);
72842                 listener.apply(this, arguments);
72843             }
72844             target.on(typeOnce, one, capture);
72845             return this;
72846         }
72847
72848         // Adapted from d3-zoom to handle pointer events.
72849
72850         // Ignore right-click, since that should open the context menu.
72851         function defaultFilter$2() {
72852           return !event.ctrlKey && !event.button;
72853         }
72854
72855         function defaultExtent$1() {
72856           var e = this;
72857           if (e instanceof SVGElement) {
72858             e = e.ownerSVGElement || e;
72859             if (e.hasAttribute('viewBox')) {
72860               e = e.viewBox.baseVal;
72861               return [[e.x, e.y], [e.x + e.width, e.y + e.height]];
72862             }
72863             return [[0, 0], [e.width.baseVal.value, e.height.baseVal.value]];
72864           }
72865           return [[0, 0], [e.clientWidth, e.clientHeight]];
72866         }
72867
72868         function defaultWheelDelta$1() {
72869           return -event.deltaY * (event.deltaMode === 1 ? 0.05 : event.deltaMode ? 1 : 0.002);
72870         }
72871
72872         function defaultConstrain$1(transform, extent, translateExtent) {
72873           var dx0 = transform.invertX(extent[0][0]) - translateExtent[0][0],
72874               dx1 = transform.invertX(extent[1][0]) - translateExtent[1][0],
72875               dy0 = transform.invertY(extent[0][1]) - translateExtent[0][1],
72876               dy1 = transform.invertY(extent[1][1]) - translateExtent[1][1];
72877           return transform.translate(
72878             dx1 > dx0 ? (dx0 + dx1) / 2 : Math.min(0, dx0) || Math.max(0, dx1),
72879             dy1 > dy0 ? (dy0 + dy1) / 2 : Math.min(0, dy0) || Math.max(0, dy1)
72880           );
72881         }
72882
72883         function utilZoomPan() {
72884           var filter = defaultFilter$2,
72885               extent = defaultExtent$1,
72886               constrain = defaultConstrain$1,
72887               wheelDelta = defaultWheelDelta$1,
72888               scaleExtent = [0, Infinity],
72889               translateExtent = [[-Infinity, -Infinity], [Infinity, Infinity]],
72890               interpolate = interpolateZoom,
72891               listeners = dispatch('start', 'zoom', 'end'),
72892               _wheelDelay = 150,
72893               _transform = identity$2,
72894               _activeGesture;
72895
72896           function zoom(selection) {
72897             selection
72898                 .on('pointerdown.zoom', pointerdown)
72899                 .on('wheel.zoom', wheeled)
72900                 .style('touch-action', 'none')
72901                 .style('-webkit-tap-highlight-color', 'rgba(0,0,0,0)');
72902
72903             select(window)
72904                 .on('pointermove.zoompan', pointermove)
72905                 .on('pointerup.zoompan pointercancel.zoompan', pointerup);
72906           }
72907
72908           zoom.transform = function(collection, transform, point) {
72909             var selection = collection.selection ? collection.selection() : collection;
72910             if (collection !== selection) {
72911               schedule(collection, transform, point);
72912             } else {
72913               selection.interrupt().each(function() {
72914                 gesture(this, arguments)
72915                     .start()
72916                     .zoom(null, typeof transform === 'function' ? transform.apply(this, arguments) : transform)
72917                     .end();
72918               });
72919             }
72920           };
72921
72922           zoom.scaleBy = function(selection, k, p) {
72923             zoom.scaleTo(selection, function() {
72924               var k0 = _transform.k,
72925                   k1 = typeof k === 'function' ? k.apply(this, arguments) : k;
72926               return k0 * k1;
72927             }, p);
72928           };
72929
72930           zoom.scaleTo = function(selection, k, p) {
72931             zoom.transform(selection, function() {
72932               var e = extent.apply(this, arguments),
72933                   t0 = _transform,
72934                   p0 = p == null ? centroid(e) : typeof p === 'function' ? p.apply(this, arguments) : p,
72935                   p1 = t0.invert(p0),
72936                   k1 = typeof k === 'function' ? k.apply(this, arguments) : k;
72937               return constrain(translate(scale(t0, k1), p0, p1), e, translateExtent);
72938             }, p);
72939           };
72940
72941           zoom.translateBy = function(selection, x, y) {
72942             zoom.transform(selection, function() {
72943               return constrain(_transform.translate(
72944                 typeof x === 'function' ? x.apply(this, arguments) : x,
72945                 typeof y === 'function' ? y.apply(this, arguments) : y
72946               ), extent.apply(this, arguments), translateExtent);
72947             });
72948           };
72949
72950           zoom.translateTo = function(selection, x, y, p) {
72951             zoom.transform(selection, function() {
72952               var e = extent.apply(this, arguments),
72953                   t = _transform,
72954                   p0 = p == null ? centroid(e) : typeof p === 'function' ? p.apply(this, arguments) : p;
72955               return constrain(identity$2.translate(p0[0], p0[1]).scale(t.k).translate(
72956                 typeof x === 'function' ? -x.apply(this, arguments) : -x,
72957                 typeof y === 'function' ? -y.apply(this, arguments) : -y
72958               ), e, translateExtent);
72959             }, p);
72960           };
72961
72962           function scale(transform, k) {
72963             k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], k));
72964             return k === transform.k ? transform : new Transform(k, transform.x, transform.y);
72965           }
72966
72967           function translate(transform, p0, p1) {
72968             var x = p0[0] - p1[0] * transform.k, y = p0[1] - p1[1] * transform.k;
72969             return x === transform.x && y === transform.y ? transform : new Transform(transform.k, x, y);
72970           }
72971
72972           function centroid(extent) {
72973             return [(+extent[0][0] + +extent[1][0]) / 2, (+extent[0][1] + +extent[1][1]) / 2];
72974           }
72975
72976           function schedule(transition, transform, point) {
72977             transition
72978                 .on('start.zoom', function() { gesture(this, arguments).start(); })
72979                 .on('interrupt.zoom end.zoom', function() { gesture(this, arguments).end(); })
72980                 .tween('zoom', function() {
72981                   var that = this,
72982                       args = arguments,
72983                       g = gesture(that, args),
72984                       e = extent.apply(that, args),
72985                       p = point == null ? centroid(e) : typeof point === 'function' ? point.apply(that, args) : point,
72986                       w = Math.max(e[1][0] - e[0][0], e[1][1] - e[0][1]),
72987                       a = _transform,
72988                       b = typeof transform === 'function' ? transform.apply(that, args) : transform,
72989                       i = interpolate(a.invert(p).concat(w / a.k), b.invert(p).concat(w / b.k));
72990                   return function(t) {
72991                     if (t === 1) { t = b; } // Avoid rounding error on end.
72992                     else { var l = i(t), k = w / l[2]; t = new Transform(k, p[0] - l[0] * k, p[1] - l[1] * k); }
72993                     g.zoom(null, t);
72994                   };
72995                 });
72996           }
72997
72998           function gesture(that, args, clean) {
72999             return (!clean && _activeGesture) || new Gesture(that, args);
73000           }
73001
73002           function Gesture(that, args) {
73003             this.that = that;
73004             this.args = args;
73005             this.active = 0;
73006             this.extent = extent.apply(that, args);
73007           }
73008
73009           Gesture.prototype = {
73010             start: function() {
73011               if (++this.active === 1) {
73012                 _activeGesture = this;
73013                 this.emit('start');
73014               }
73015               return this;
73016             },
73017             zoom: function(key, transform) {
73018               if (this.mouse && key !== 'mouse') { this.mouse[1] = transform.invert(this.mouse[0]); }
73019               if (this.pointer0 && key !== 'touch') { this.pointer0[1] = transform.invert(this.pointer0[0]); }
73020               if (this.pointer1 && key !== 'touch') { this.pointer1[1] = transform.invert(this.pointer1[0]); }
73021               _transform = transform;
73022               this.emit('zoom');
73023               return this;
73024             },
73025             end: function() {
73026               if (--this.active === 0) {
73027                 _activeGesture = null;
73028                 this.emit('end');
73029               }
73030               return this;
73031             },
73032             emit: function(type) {
73033               customEvent(new ZoomEvent(zoom, type, _transform), listeners.apply, listeners, [type, this.that, this.args]);
73034             }
73035           };
73036
73037           function wheeled() {
73038             if (!filter.apply(this, arguments)) { return; }
73039             var g = gesture(this, arguments),
73040                 t = _transform,
73041                 k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], t.k * Math.pow(2, wheelDelta.apply(this, arguments)))),
73042                 p = utilFastMouse(this)(event);
73043
73044             // If the mouse is in the same location as before, reuse it.
73045             // If there were recent wheel events, reset the wheel idle timeout.
73046             if (g.wheel) {
73047               if (g.mouse[0][0] !== p[0] || g.mouse[0][1] !== p[1]) {
73048                 g.mouse[1] = t.invert(g.mouse[0] = p);
73049               }
73050               clearTimeout(g.wheel);
73051
73052             // Otherwise, capture the mouse point and location at the start.
73053             } else {
73054               g.mouse = [p, t.invert(p)];
73055               interrupt(this);
73056               g.start();
73057             }
73058
73059             event.preventDefault();
73060             event.stopImmediatePropagation();
73061             g.wheel = setTimeout(wheelidled, _wheelDelay);
73062             g.zoom('mouse', constrain(translate(scale(t, k), g.mouse[0], g.mouse[1]), g.extent, translateExtent));
73063
73064             function wheelidled() {
73065               g.wheel = null;
73066               g.end();
73067             }
73068           }
73069
73070           var _downPointerIDs = new Set();
73071           var _pointerLocGetter;
73072
73073           function pointerdown() {
73074             _downPointerIDs.add(event.pointerId);
73075
73076             if (!filter.apply(this, arguments)) { return; }
73077
73078             var g = gesture(this, arguments, _downPointerIDs.size === 1);
73079             var started;
73080
73081             event.stopImmediatePropagation();
73082             _pointerLocGetter = utilFastMouse(this);
73083             var loc = _pointerLocGetter(event);
73084             var p = [loc, _transform.invert(loc), event.pointerId];
73085             if (!g.pointer0) {
73086                g.pointer0 = p;
73087                started = true;
73088
73089             } else if (!g.pointer1 && g.pointer0[2] !== p[2]) {
73090                g.pointer1 = p;
73091             }
73092
73093             if (started) {
73094               interrupt(this);
73095               g.start();
73096             }
73097           }
73098
73099           function pointermove() {
73100             if (!_downPointerIDs.has(event.pointerId)) { return; }
73101
73102             if (!_activeGesture || !_pointerLocGetter) { return; }
73103
73104             var g = gesture(this, arguments);
73105
73106             var isPointer0 = g.pointer0 && g.pointer0[2] === event.pointerId;
73107             var isPointer1 = !isPointer0 && g.pointer1 && g.pointer1[2] === event.pointerId;
73108
73109             if ((isPointer0 || isPointer1) && 'buttons' in event && !event.buttons) {
73110               // The pointer went up without ending the gesture somehow, e.g.
73111               // a down mouse was moved off the map and released. End it here.
73112               if (g.pointer0) { _downPointerIDs.delete(g.pointer0[2]); }
73113               if (g.pointer1) { _downPointerIDs.delete(g.pointer1[2]); }
73114               g.end();
73115               return;
73116             }
73117
73118             event.preventDefault();
73119             event.stopImmediatePropagation();
73120
73121             var loc = _pointerLocGetter(event);
73122             var t, p, l;
73123
73124             if (isPointer0) { g.pointer0[0] = loc; }
73125             else if (isPointer1) { g.pointer1[0] = loc; }
73126
73127             t = _transform;
73128             if (g.pointer1) {
73129               var p0 = g.pointer0[0], l0 = g.pointer0[1],
73130                   p1 = g.pointer1[0], l1 = g.pointer1[1],
73131                   dp = (dp = p1[0] - p0[0]) * dp + (dp = p1[1] - p0[1]) * dp,
73132                   dl = (dl = l1[0] - l0[0]) * dl + (dl = l1[1] - l0[1]) * dl;
73133               t = scale(t, Math.sqrt(dp / dl));
73134               p = [(p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2];
73135               l = [(l0[0] + l1[0]) / 2, (l0[1] + l1[1]) / 2];
73136             } else if (g.pointer0) {
73137               p = g.pointer0[0];
73138               l = g.pointer0[1];
73139             }
73140             else { return; }
73141             g.zoom('touch', constrain(translate(t, p, l), g.extent, translateExtent));
73142           }
73143
73144           function pointerup() {
73145             if (!_downPointerIDs.has(event.pointerId)) { return; }
73146
73147             _downPointerIDs.delete(event.pointerId);
73148
73149             if (!_activeGesture) { return; }
73150
73151             var g = gesture(this, arguments);
73152
73153             event.stopImmediatePropagation();
73154
73155             if (g.pointer0 && g.pointer0[2] === event.pointerId) { delete g.pointer0; }
73156             else if (g.pointer1 && g.pointer1[2] === event.pointerId) { delete g.pointer1; }
73157
73158             if (g.pointer1 && !g.pointer0) {
73159               g.pointer0 = g.pointer1;
73160               delete g.pointer1;
73161             }
73162             if (g.pointer0) { g.pointer0[1] = _transform.invert(g.pointer0[0]); }
73163             else {
73164               g.end();
73165             }
73166           }
73167
73168           zoom.wheelDelta = function(_) {
73169             return arguments.length ? (wheelDelta = utilFunctor(+_), zoom) : wheelDelta;
73170           };
73171
73172           zoom.filter = function(_) {
73173             return arguments.length ? (filter = utilFunctor(!!_), zoom) : filter;
73174           };
73175
73176           zoom.extent = function(_) {
73177             return arguments.length ? (extent = utilFunctor([[+_[0][0], +_[0][1]], [+_[1][0], +_[1][1]]]), zoom) : extent;
73178           };
73179
73180           zoom.scaleExtent = function(_) {
73181             return arguments.length ? (scaleExtent[0] = +_[0], scaleExtent[1] = +_[1], zoom) : [scaleExtent[0], scaleExtent[1]];
73182           };
73183
73184           zoom.translateExtent = function(_) {
73185             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]]];
73186           };
73187
73188           zoom.constrain = function(_) {
73189             return arguments.length ? (constrain = _, zoom) : constrain;
73190           };
73191
73192           zoom.interpolate = function(_) {
73193             return arguments.length ? (interpolate = _, zoom) : interpolate;
73194           };
73195
73196           zoom._transform = function(_) {
73197             return arguments.length ? (_transform = _, zoom) : _transform;
73198           };
73199
73200           zoom.on = function() {
73201             var value = listeners.on.apply(listeners, arguments);
73202             return value === listeners ? zoom : value;
73203           };
73204
73205           return zoom;
73206         }
73207
73208         // A custom double-click / double-tap event detector that works on touch devices
73209         // if pointer events are supported. Falls back to default `dblclick` event.
73210         function utilDoubleUp() {
73211
73212             var dispatch$1 = dispatch('doubleUp');
73213
73214             var _maxTimespan = 500; // milliseconds
73215             var _maxDistance = 20; // web pixels; be somewhat generous to account for touch devices
73216             var _pointer; // object representing the pointer that could trigger double up
73217
73218             function pointerIsValidFor(loc) {
73219                 // second pointerup must occur within a small timeframe after the first pointerdown
73220                 return new Date().getTime() - _pointer.startTime <= _maxTimespan &&
73221                     // all pointer events must occur within a small distance of the first pointerdown
73222                     geoVecLength(_pointer.startLoc, loc) <= _maxDistance;
73223             }
73224
73225             function pointerdown() {
73226
73227                 // ignore right-click
73228                 if (event.ctrlKey || event.button === 2) { return; }
73229
73230                 var loc = [event.clientX, event.clientY];
73231
73232                 // Don't rely on pointerId here since it can change between pointerdown
73233                 // events on touch devices
73234                 if (_pointer && !pointerIsValidFor(loc)) {
73235                     // if this pointer is no longer valid, clear it so another can be started
73236                     _pointer = undefined;
73237                 }
73238
73239                 if (!_pointer) {
73240                     _pointer = {
73241                         startLoc: loc,
73242                         startTime: new Date().getTime(),
73243                         upCount: 0,
73244                         pointerId: event.pointerId
73245                     };
73246                 } else { // double down
73247                     _pointer.pointerId = event.pointerId;
73248                 }
73249             }
73250
73251             function pointerup() {
73252
73253                 // ignore right-click
73254                 if (event.ctrlKey || event.button === 2) { return; }
73255
73256                 if (!_pointer || _pointer.pointerId !== event.pointerId) { return; }
73257
73258                 _pointer.upCount += 1;
73259
73260                 if (_pointer.upCount === 2) { // double up!
73261                     var loc = [event.clientX, event.clientY];
73262                     if (pointerIsValidFor(loc)) {
73263                         var locInThis = utilFastMouse(this)(event);
73264                         dispatch$1.call('doubleUp', this, locInThis);
73265                     }
73266                     // clear the pointer info in any case
73267                     _pointer = undefined;
73268                 }
73269             }
73270
73271             function doubleUp(selection) {
73272                 if ('PointerEvent' in window) {
73273                     // dblclick isn't well supported on touch devices so manually use
73274                     // pointer events if they're available
73275                     selection
73276                         .on('pointerdown.doubleUp', pointerdown)
73277                         .on('pointerup.doubleUp', pointerup);
73278                 } else {
73279                     // fallback to dblclick
73280                     selection
73281                         .on('dblclick.doubleUp', function() {
73282                             dispatch$1.call('doubleUp', this, utilFastMouse(this)(event));
73283                         });
73284                 }
73285             }
73286
73287             doubleUp.off = function(selection) {
73288                 selection
73289                     .on('pointerdown.doubleUp', null)
73290                     .on('pointerup.doubleUp', null)
73291                     .on('dblclick.doubleUp', null);
73292             };
73293
73294             return utilRebind(doubleUp, dispatch$1, 'on');
73295         }
73296
73297         // constants
73298         var TILESIZE = 256;
73299         var minZoom = 2;
73300         var maxZoom = 24;
73301         var kMin = geoZoomToScale(minZoom, TILESIZE);
73302         var kMax = geoZoomToScale(maxZoom, TILESIZE);
73303
73304         function clamp(num, min, max) {
73305             return Math.max(min, Math.min(num, max));
73306         }
73307
73308
73309         function rendererMap(context) {
73310             var dispatch$1 = dispatch(
73311                 'move', 'drawn',
73312                 'crossEditableZoom', 'hitMinZoom',
73313                 'changeHighlighting', 'changeAreaFill'
73314             );
73315             var projection = context.projection;
73316             var curtainProjection = context.curtainProjection;
73317             var drawLayers;
73318             var drawPoints;
73319             var drawVertices;
73320             var drawLines;
73321             var drawAreas;
73322             var drawMidpoints;
73323             var drawLabels;
73324
73325             var _selection = select(null);
73326             var supersurface = select(null);
73327             var wrapper = select(null);
73328             var surface = select(null);
73329
73330             var _dimensions = [1, 1];
73331             var _dblClickZoomEnabled = true;
73332             var _redrawEnabled = true;
73333             var _gestureTransformStart;
73334             var _transformStart = projection.transform();
73335             var _transformLast;
73336             var _isTransformed = false;
73337             var _minzoom = 0;
73338             var _getMouseCoords;
73339             var _lastPointerEvent;
73340             var _lastWithinEditableZoom;
73341
73342             // whether a pointerdown event started the zoom
73343             var _pointerDown = false;
73344
73345             // use pointer events on supported platforms; fallback to mouse events
73346             var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
73347
73348             // use pointer event interaction if supported; fallback to touch/mouse events in d3-zoom
73349             var _zoomerPannerFunction = 'PointerEvent' in window ? utilZoomPan : d3_zoom;
73350
73351             var _zoomerPanner = _zoomerPannerFunction()
73352                 .scaleExtent([kMin, kMax])
73353                 .interpolate(interpolate)
73354                 .filter(zoomEventFilter)
73355                 .on('zoom.map', zoomPan)
73356                 .on('start.map', function() {
73357                     _pointerDown = event.sourceEvent && event.sourceEvent.type === 'pointerdown';
73358                 })
73359                 .on('end.map', function() {
73360                     _pointerDown = false;
73361                 });
73362             var _doubleUpHandler = utilDoubleUp();
73363
73364             var scheduleRedraw = throttle(redraw, 750);
73365             // var isRedrawScheduled = false;
73366             // var pendingRedrawCall;
73367             // function scheduleRedraw() {
73368             //     // Only schedule the redraw if one has not already been set.
73369             //     if (isRedrawScheduled) return;
73370             //     isRedrawScheduled = true;
73371             //     var that = this;
73372             //     var args = arguments;
73373             //     pendingRedrawCall = window.requestIdleCallback(function () {
73374             //         // Reset the boolean so future redraws can be set.
73375             //         isRedrawScheduled = false;
73376             //         redraw.apply(that, args);
73377             //     }, { timeout: 1400 });
73378             // }
73379
73380             function cancelPendingRedraw() {
73381                 scheduleRedraw.cancel();
73382                 // isRedrawScheduled = false;
73383                 // window.cancelIdleCallback(pendingRedrawCall);
73384             }
73385
73386
73387             function map(selection) {
73388                 _selection = selection;
73389
73390                 context
73391                     .on('change.map', immediateRedraw);
73392
73393                 var osm = context.connection();
73394                 if (osm) {
73395                     osm.on('change.map', immediateRedraw);
73396                 }
73397
73398                 function didUndoOrRedo(targetTransform) {
73399                     var mode = context.mode().id;
73400                     if (mode !== 'browse' && mode !== 'select') { return; }
73401                     if (targetTransform) {
73402                         map.transformEase(targetTransform);
73403                     }
73404                 }
73405
73406                 context.history()
73407                     .on('merge.map', function() { scheduleRedraw(); })
73408                     .on('change.map', immediateRedraw)
73409                     .on('undone.map', function(stack, fromStack) {
73410                         didUndoOrRedo(fromStack.transform);
73411                     })
73412                     .on('redone.map', function(stack) {
73413                         didUndoOrRedo(stack.transform);
73414                     });
73415
73416                 context.background()
73417                     .on('change.map', immediateRedraw);
73418
73419                 context.features()
73420                     .on('redraw.map', immediateRedraw);
73421
73422                 drawLayers
73423                     .on('change.map', function() {
73424                         context.background().updateImagery();
73425                         immediateRedraw();
73426                     });
73427
73428                 selection
73429                     .on('wheel.map mousewheel.map', function() {
73430                         // disable swipe-to-navigate browser pages on trackpad/magic mouse – #5552
73431                         event.preventDefault();
73432                     })
73433                     .call(_zoomerPanner)
73434                     .call(_zoomerPanner.transform, projection.transform())
73435                     .on('dblclick.zoom', null); // override d3-zoom dblclick handling
73436
73437                 map.supersurface = supersurface = selection.append('div')
73438                     .attr('class', 'supersurface')
73439                     .call(utilSetTransform, 0, 0);
73440
73441                 // Need a wrapper div because Opera can't cope with an absolutely positioned
73442                 // SVG element: http://bl.ocks.org/jfirebaugh/6fbfbd922552bf776c16
73443                 wrapper = supersurface
73444                     .append('div')
73445                     .attr('class', 'layer layer-data');
73446
73447                 map.surface = surface = wrapper
73448                     .call(drawLayers)
73449                     .selectAll('.surface');
73450
73451                 surface
73452                     .call(drawLabels.observe)
73453                     .call(_doubleUpHandler)
73454                     .on(_pointerPrefix + 'down.zoom', function() {
73455                         _lastPointerEvent = event;
73456                         if (event.button === 2) {
73457                             event.stopPropagation();
73458                         }
73459                     }, true)
73460                     .on(_pointerPrefix + 'up.zoom', function() {
73461                         _lastPointerEvent = event;
73462                         if (resetTransform()) {
73463                             immediateRedraw();
73464                         }
73465                     })
73466                     .on(_pointerPrefix + 'move.map', function() {
73467                         _lastPointerEvent = event;
73468                     })
73469                     .on(_pointerPrefix + 'over.vertices', function() {
73470                         if (map.editableDataEnabled() && !_isTransformed) {
73471                             var hover = event.target.__data__;
73472                             surface.call(drawVertices.drawHover, context.graph(), hover, map.extent());
73473                             dispatch$1.call('drawn', this, { full: false });
73474                         }
73475                     })
73476                     .on(_pointerPrefix + 'out.vertices', function() {
73477                         if (map.editableDataEnabled() && !_isTransformed) {
73478                             var hover = event.relatedTarget && event.relatedTarget.__data__;
73479                             surface.call(drawVertices.drawHover, context.graph(), hover, map.extent());
73480                             dispatch$1.call('drawn', this, { full: false });
73481                         }
73482                     });
73483
73484                 var detected = utilDetect();
73485
73486                 // only WebKit supports gesture events
73487                 if ('GestureEvent' in window &&
73488                     // Listening for gesture events on iOS 13.4+ breaks double-tapping,
73489                     // but we only need to do this on desktop Safari anyway. – #7694
73490                     !detected.isMobileWebKit) {
73491
73492                     // Desktop Safari sends gesture events for multitouch trackpad pinches.
73493                     // We can listen for these and translate them into map zooms.
73494                     surface
73495                         .on('gesturestart.surface', function() {
73496                             event.preventDefault();
73497                             _gestureTransformStart = projection.transform();
73498                         })
73499                         .on('gesturechange.surface', gestureChange);
73500                 }
73501
73502                 // must call after surface init
73503                 updateAreaFill();
73504
73505                 _doubleUpHandler.on('doubleUp.map', function(p0) {
73506                     if (!_dblClickZoomEnabled) { return; }
73507
73508                     // don't zoom if targeting something other than the map itself
73509                     if (typeof event.target.__data__ === 'object' &&
73510                         // or area fills
73511                         !select(event.target).classed('fill')) { return; }
73512
73513                     var zoomOut = event.shiftKey;
73514
73515                     var t = projection.transform();
73516
73517                     var p1 = t.invert(p0);
73518
73519                     t = t.scale(zoomOut ? 0.5 : 2);
73520
73521                     t.x = p0[0] - p1[0] * t.k;
73522                     t.y = p0[1] - p1[1] * t.k;
73523
73524                     map.transformEase(t);
73525                 });
73526
73527                 context.on('enter.map',  function() {
73528                     if (!map.editableDataEnabled(true /* skip zoom check */)) { return; }
73529
73530                     // redraw immediately any objects affected by a change in selectedIDs.
73531                     var graph = context.graph();
73532                     var selectedAndParents = {};
73533                     context.selectedIDs().forEach(function(id) {
73534                         var entity = graph.hasEntity(id);
73535                         if (entity) {
73536                             selectedAndParents[entity.id] = entity;
73537                             if (entity.type === 'node') {
73538                                 graph.parentWays(entity).forEach(function(parent) {
73539                                     selectedAndParents[parent.id] = parent;
73540                                 });
73541                             }
73542                         }
73543                     });
73544                     var data = Object.values(selectedAndParents);
73545                     var filter = function(d) { return d.id in selectedAndParents; };
73546
73547                     data = context.features().filter(data, graph);
73548
73549                     surface
73550                         .call(drawVertices.drawSelected, graph, map.extent())
73551                         .call(drawLines, graph, data, filter)
73552                         .call(drawAreas, graph, data, filter)
73553                         .call(drawMidpoints, graph, data, filter, map.trimmedExtent());
73554
73555                     dispatch$1.call('drawn', this, { full: false });
73556
73557                     // redraw everything else later
73558                     scheduleRedraw();
73559                 });
73560
73561                 map.dimensions(utilGetDimensions(selection));
73562             }
73563
73564
73565             function zoomEventFilter() {
73566                 // Fix for #2151, (see also d3/d3-zoom#60, d3/d3-brush#18)
73567                 // Intercept `mousedown` and check if there is an orphaned zoom gesture.
73568                 // This can happen if a previous `mousedown` occurred without a `mouseup`.
73569                 // If we detect this, dispatch `mouseup` to complete the orphaned gesture,
73570                 // so that d3-zoom won't stop propagation of new `mousedown` events.
73571                 if (event.type === 'mousedown') {
73572                     var hasOrphan = false;
73573                     var listeners = window.__on;
73574                     for (var i = 0; i < listeners.length; i++) {
73575                         var listener = listeners[i];
73576                         if (listener.name === 'zoom' && listener.type === 'mouseup') {
73577                             hasOrphan = true;
73578                             break;
73579                         }
73580                     }
73581                     if (hasOrphan) {
73582                         var event$1 = window.CustomEvent;
73583                         if (event$1) {
73584                             event$1 = new event$1('mouseup');
73585                         } else {
73586                             event$1 = window.document.createEvent('Event');
73587                             event$1.initEvent('mouseup', false, false);
73588                         }
73589                         // Event needs to be dispatched with an event.view property.
73590                         event$1.view = window;
73591                         window.dispatchEvent(event$1);
73592                     }
73593                 }
73594
73595                 return event.button !== 2;   // ignore right clicks
73596             }
73597
73598
73599             function pxCenter() {
73600                 return [_dimensions[0] / 2, _dimensions[1] / 2];
73601             }
73602
73603
73604             function drawEditable(difference, extent) {
73605                 var mode = context.mode();
73606                 var graph = context.graph();
73607                 var features = context.features();
73608                 var all = context.history().intersects(map.extent());
73609                 var fullRedraw = false;
73610                 var data;
73611                 var set;
73612                 var filter;
73613                 var applyFeatureLayerFilters = true;
73614
73615                 if (map.isInWideSelection()) {
73616                     data = [];
73617                     utilEntityAndDeepMemberIDs(mode.selectedIDs(), context.graph()).forEach(function(id) {
73618                         var entity = context.hasEntity(id);
73619                         if (entity) { data.push(entity); }
73620                     });
73621                     fullRedraw = true;
73622                     filter = utilFunctor(true);
73623                     // selected features should always be visible, so we can skip filtering
73624                     applyFeatureLayerFilters = false;
73625
73626                 } else if (difference) {
73627                     var complete = difference.complete(map.extent());
73628                     data = Object.values(complete).filter(Boolean);
73629                     set = new Set(Object.keys(complete));
73630                     filter = function(d) { return set.has(d.id); };
73631                     features.clear(data);
73632
73633                 } else {
73634                     // force a full redraw if gatherStats detects that a feature
73635                     // should be auto-hidden (e.g. points or buildings)..
73636                     if (features.gatherStats(all, graph, _dimensions)) {
73637                         extent = undefined;
73638                     }
73639
73640                     if (extent) {
73641                         data = context.history().intersects(map.extent().intersection(extent));
73642                         set = new Set(data.map(function(entity) { return entity.id; }));
73643                         filter = function(d) { return set.has(d.id); };
73644
73645                     } else {
73646                         data = all;
73647                         fullRedraw = true;
73648                         filter = utilFunctor(true);
73649                     }
73650                 }
73651
73652                 if (applyFeatureLayerFilters) {
73653                     data = features.filter(data, graph);
73654                 } else {
73655                     context.features().resetStats();
73656                 }
73657
73658                 if (mode && mode.id === 'select') {
73659                     // update selected vertices - the user might have just double-clicked a way,
73660                     // creating a new vertex, triggering a partial redraw without a mode change
73661                     surface.call(drawVertices.drawSelected, graph, map.extent());
73662                 }
73663
73664                 surface
73665                     .call(drawVertices, graph, data, filter, map.extent(), fullRedraw)
73666                     .call(drawLines, graph, data, filter)
73667                     .call(drawAreas, graph, data, filter)
73668                     .call(drawMidpoints, graph, data, filter, map.trimmedExtent())
73669                     .call(drawLabels, graph, data, filter, _dimensions, fullRedraw)
73670                     .call(drawPoints, graph, data, filter);
73671
73672                 dispatch$1.call('drawn', this, {full: true});
73673             }
73674
73675             map.init = function() {
73676                 drawLayers = svgLayers(projection, context);
73677                 drawPoints = svgPoints(projection, context);
73678                 drawVertices = svgVertices(projection, context);
73679                 drawLines = svgLines(projection, context);
73680                 drawAreas = svgAreas(projection, context);
73681                 drawMidpoints = svgMidpoints(projection, context);
73682                 drawLabels = svgLabels(projection, context);
73683             };
73684
73685             function editOff() {
73686                 context.features().resetStats();
73687                 surface.selectAll('.layer-osm *').remove();
73688                 surface.selectAll('.layer-touch:not(.markers) *').remove();
73689
73690                 var allowed = {
73691                     'browse': true,
73692                     'save': true,
73693                     'select-note': true,
73694                     'select-data': true,
73695                     'select-error': true
73696                 };
73697
73698                 var mode = context.mode();
73699                 if (mode && !allowed[mode.id]) {
73700                     context.enter(modeBrowse(context));
73701                 }
73702
73703                 dispatch$1.call('drawn', this, {full: true});
73704             }
73705
73706
73707
73708
73709
73710             function gestureChange() {
73711                 // Remap Safari gesture events to wheel events - #5492
73712                 // We want these disabled most places, but enabled for zoom/unzoom on map surface
73713                 // https://developer.mozilla.org/en-US/docs/Web/API/GestureEvent
73714                 var e = event;
73715                 e.preventDefault();
73716
73717                 var props = {
73718                     deltaMode: 0,    // dummy values to ignore in zoomPan
73719                     deltaY: 1,       // dummy values to ignore in zoomPan
73720                     clientX: e.clientX,
73721                     clientY: e.clientY,
73722                     screenX: e.screenX,
73723                     screenY: e.screenY,
73724                     x: e.x,
73725                     y: e.y
73726                 };
73727
73728                 var e2 = new WheelEvent('wheel', props);
73729                 e2._scale = e.scale;         // preserve the original scale
73730                 e2._rotation = e.rotation;   // preserve the original rotation
73731
73732                 _selection.node().dispatchEvent(e2);
73733             }
73734
73735
73736             function zoomPan(manualEvent) {
73737                 var event$1 = (manualEvent || event);
73738                 var source = event$1.sourceEvent;
73739                 var eventTransform = event$1.transform;
73740                 var x = eventTransform.x;
73741                 var y = eventTransform.y;
73742                 var k = eventTransform.k;
73743
73744                 // Special handling of 'wheel' events:
73745                 // They might be triggered by the user scrolling the mouse wheel,
73746                 // or 2-finger pinch/zoom gestures, the transform may need adjustment.
73747                 if (source && source.type === 'wheel') {
73748
73749                     // assume that the gesture is already handled by pointer events
73750                     if (_pointerDown) { return; }
73751
73752                     var detected = utilDetect();
73753                     var dX = source.deltaX;
73754                     var dY = source.deltaY;
73755                     var x2 = x;
73756                     var y2 = y;
73757                     var k2 = k;
73758                     var t0, p0, p1;
73759
73760                     // Normalize mousewheel scroll speed (Firefox) - #3029
73761                     // If wheel delta is provided in LINE units, recalculate it in PIXEL units
73762                     // We are essentially redoing the calculations that occur here:
73763                     //   https://github.com/d3/d3-zoom/blob/78563a8348aa4133b07cac92e2595c2227ca7cd7/src/zoom.js#L203
73764                     // See this for more info:
73765                     //   https://github.com/basilfx/normalize-wheel/blob/master/src/normalizeWheel.js
73766                     if (source.deltaMode === 1 /* LINE */) {
73767                         // Convert from lines to pixels, more if the user is scrolling fast.
73768                         // (I made up the exp function to roughly match Firefox to what Chrome does)
73769                         // These numbers should be floats, because integers are treated as pan gesture below.
73770                         var lines = Math.abs(source.deltaY);
73771                         var sign = (source.deltaY > 0) ? 1 : -1;
73772                         dY = sign * clamp(
73773                             Math.exp((lines - 1) * 0.75) * 4.000244140625,
73774                             4.000244140625,    // min
73775                             350.000244140625   // max
73776                         );
73777
73778                         // On Firefox Windows and Linux we always get +/- the scroll line amount (default 3)
73779                         // There doesn't seem to be any scroll accelleration.
73780                         // This multiplier increases the speed a little bit - #5512
73781                         if (detected.os !== 'mac') {
73782                             dY *= 5;
73783                         }
73784
73785                         // recalculate x2,y2,k2
73786                         t0 = _isTransformed ? _transformLast : _transformStart;
73787                         p0 = _getMouseCoords(source);
73788                         p1 = t0.invert(p0);
73789                         k2 = t0.k * Math.pow(2, -dY / 500);
73790                         k2 = clamp(k2, kMin, kMax);
73791                         x2 = p0[0] - p1[0] * k2;
73792                         y2 = p0[1] - p1[1] * k2;
73793
73794                     // 2 finger map pinch zooming (Safari) - #5492
73795                     // These are fake `wheel` events we made from Safari `gesturechange` events..
73796                     } else if (source._scale) {
73797                         // recalculate x2,y2,k2
73798                         t0 = _gestureTransformStart;
73799                         p0 = _getMouseCoords(source);
73800                         p1 = t0.invert(p0);
73801                         k2 = t0.k * source._scale;
73802                         k2 = clamp(k2, kMin, kMax);
73803                         x2 = p0[0] - p1[0] * k2;
73804                         y2 = p0[1] - p1[1] * k2;
73805
73806                     // 2 finger map pinch zooming (all browsers except Safari) - #5492
73807                     // Pinch zooming via the `wheel` event will always have:
73808                     // - `ctrlKey = true`
73809                     // - `deltaY` is not round integer pixels (ignore `deltaX`)
73810                     } else if (source.ctrlKey && !isInteger(dY)) {
73811                         dY *= 6;   // slightly scale up whatever the browser gave us
73812
73813                         // recalculate x2,y2,k2
73814                         t0 = _isTransformed ? _transformLast : _transformStart;
73815                         p0 = _getMouseCoords(source);
73816                         p1 = t0.invert(p0);
73817                         k2 = t0.k * Math.pow(2, -dY / 500);
73818                         k2 = clamp(k2, kMin, kMax);
73819                         x2 = p0[0] - p1[0] * k2;
73820                         y2 = p0[1] - p1[1] * k2;
73821
73822                     // Trackpad scroll zooming with shift or alt/option key down
73823                     } else if ((source.altKey || source.shiftKey) && isInteger(dY)) {
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 panning (Mac only, all browsers) - #5492, #5512
73834                     // Panning via the `wheel` event will always have:
73835                     // - `ctrlKey = false`
73836                     // - `deltaX`,`deltaY` are round integer pixels
73837                     } else if (detected.os === 'mac' && !source.ctrlKey && isInteger(dX) && isInteger(dY)) {
73838                         p1 = projection.translate();
73839                         x2 = p1[0] - dX;
73840                         y2 = p1[1] - dY;
73841                         k2 = projection.scale();
73842                         k2 = clamp(k2, kMin, kMax);
73843                     }
73844
73845                     // something changed - replace the event transform
73846                     if (x2 !== x || y2 !== y || k2 !== k) {
73847                         x = x2;
73848                         y = y2;
73849                         k = k2;
73850                         eventTransform = identity$2.translate(x2, y2).scale(k2);
73851                         if (_zoomerPanner._transform) {
73852                             // utilZoomPan interface
73853                             _zoomerPanner._transform(eventTransform);
73854                         } else {
73855                             // d3_zoom interface
73856                             _selection.node().__zoom = eventTransform;
73857                         }
73858                     }
73859
73860                 }
73861
73862                 if (_transformStart.x === x &&
73863                     _transformStart.y === y &&
73864                     _transformStart.k === k) {
73865                     return;  // no change
73866                 }
73867
73868                 var withinEditableZoom = map.withinEditableZoom();
73869                 if (_lastWithinEditableZoom !== withinEditableZoom) {
73870                     if (_lastWithinEditableZoom !== undefined) {
73871                         // notify that the map zoomed in or out over the editable zoom threshold
73872                         dispatch$1.call('crossEditableZoom', this, withinEditableZoom);
73873                     }
73874                     _lastWithinEditableZoom = withinEditableZoom;
73875                 }
73876
73877                 if (geoScaleToZoom(k, TILESIZE) < _minzoom) {
73878                     surface.interrupt();
73879                     dispatch$1.call('hitMinZoom', this, map);
73880                     setCenterZoom(map.center(), context.minEditableZoom(), 0, true);
73881                     scheduleRedraw();
73882                     dispatch$1.call('move', this, map);
73883                     return;
73884                 }
73885
73886                 projection.transform(eventTransform);
73887
73888                 var scale = k / _transformStart.k;
73889                 var tX = (x / scale - _transformStart.x) * scale;
73890                 var tY = (y / scale - _transformStart.y) * scale;
73891
73892                 if (context.inIntro()) {
73893                     curtainProjection.transform({
73894                         x: x - tX,
73895                         y: y - tY,
73896                         k: k
73897                     });
73898                 }
73899
73900                 if (source) {
73901                     _lastPointerEvent = event$1;
73902                 }
73903                 _isTransformed = true;
73904                 _transformLast = eventTransform;
73905                 utilSetTransform(supersurface, tX, tY, scale);
73906                 scheduleRedraw();
73907
73908                 dispatch$1.call('move', this, map);
73909
73910
73911                 function isInteger(val) {
73912                     return typeof val === 'number' && isFinite(val) && Math.floor(val) === val;
73913                 }
73914             }
73915
73916
73917             function resetTransform() {
73918                 if (!_isTransformed) { return false; }
73919
73920                 utilSetTransform(supersurface, 0, 0);
73921                 _isTransformed = false;
73922                 if (context.inIntro()) {
73923                     curtainProjection.transform(projection.transform());
73924                 }
73925                 return true;
73926             }
73927
73928
73929             function redraw(difference, extent) {
73930                 if (surface.empty() || !_redrawEnabled) { return; }
73931
73932                 // If we are in the middle of a zoom/pan, we can't do differenced redraws.
73933                 // It would result in artifacts where differenced entities are redrawn with
73934                 // one transform and unchanged entities with another.
73935                 if (resetTransform()) {
73936                     difference = extent = undefined;
73937                 }
73938
73939                 var zoom = map.zoom();
73940                 var z = String(~~zoom);
73941
73942                 if (surface.attr('data-zoom') !== z) {
73943                     surface.attr('data-zoom', z);
73944                 }
73945
73946                 // class surface as `lowzoom` around z17-z18.5 (based on latitude)
73947                 var lat = map.center()[1];
73948                 var lowzoom = linear$2()
73949                     .domain([-60, 0, 60])
73950                     .range([17, 18.5, 17])
73951                     .clamp(true);
73952
73953                 surface
73954                     .classed('low-zoom', zoom <= lowzoom(lat));
73955
73956
73957                 if (!difference) {
73958                     supersurface.call(context.background());
73959                     wrapper.call(drawLayers);
73960                 }
73961
73962                 // OSM
73963                 if (map.editableDataEnabled() || map.isInWideSelection()) {
73964                     context.loadTiles(projection);
73965                     drawEditable(difference, extent);
73966                 } else {
73967                     editOff();
73968                 }
73969
73970                 _transformStart = projection.transform();
73971
73972                 return map;
73973             }
73974
73975
73976
73977             var immediateRedraw = function(difference, extent) {
73978                 if (!difference && !extent) { cancelPendingRedraw(); }
73979                 redraw(difference, extent);
73980             };
73981
73982
73983             map.lastPointerEvent = function() {
73984                 return _lastPointerEvent;
73985             };
73986
73987
73988             map.mouse = function() {
73989                 var event$1 = _lastPointerEvent || event;
73990                 if (event$1) {
73991                     var s;
73992                     while ((s = event$1.sourceEvent)) { event$1 = s; }
73993                     return _getMouseCoords(event$1);
73994                 }
73995                 return null;
73996             };
73997
73998
73999             // returns Lng/Lat
74000             map.mouseCoordinates = function() {
74001                 var coord = map.mouse() || pxCenter();
74002                 return projection.invert(coord);
74003             };
74004
74005
74006             map.dblclickZoomEnable = function(val) {
74007                 if (!arguments.length) { return _dblClickZoomEnabled; }
74008                 _dblClickZoomEnabled = val;
74009                 return map;
74010             };
74011
74012
74013             map.redrawEnable = function(val) {
74014                 if (!arguments.length) { return _redrawEnabled; }
74015                 _redrawEnabled = val;
74016                 return map;
74017             };
74018
74019
74020             map.isTransformed = function() {
74021                 return _isTransformed;
74022             };
74023
74024
74025             function setTransform(t2, duration, force) {
74026                 var t = projection.transform();
74027                 if (!force && t2.k === t.k && t2.x === t.x && t2.y === t.y) { return false; }
74028
74029                 if (duration) {
74030                     _selection
74031                         .transition()
74032                         .duration(duration)
74033                         .on('start', function() { map.startEase(); })
74034                         .call(_zoomerPanner.transform, identity$2.translate(t2.x, t2.y).scale(t2.k));
74035                 } else {
74036                     projection.transform(t2);
74037                     _transformStart = t2;
74038                     _selection.call(_zoomerPanner.transform, _transformStart);
74039                 }
74040
74041                 return true;
74042             }
74043
74044
74045             function setCenterZoom(loc2, z2, duration, force) {
74046                 var c = map.center();
74047                 var z = map.zoom();
74048                 if (loc2[0] === c[0] && loc2[1] === c[1] && z2 === z && !force) { return false; }
74049
74050                 var proj = geoRawMercator().transform(projection.transform());  // copy projection
74051
74052                 var k2 = clamp(geoZoomToScale(z2, TILESIZE), kMin, kMax);
74053                 proj.scale(k2);
74054
74055                 var t = proj.translate();
74056                 var point = proj(loc2);
74057
74058                 var center = pxCenter();
74059                 t[0] += center[0] - point[0];
74060                 t[1] += center[1] - point[1];
74061
74062                 return setTransform(identity$2.translate(t[0], t[1]).scale(k2), duration, force);
74063             }
74064
74065
74066             map.pan = function(delta, duration) {
74067                 var t = projection.translate();
74068                 var k = projection.scale();
74069
74070                 t[0] += delta[0];
74071                 t[1] += delta[1];
74072
74073                 if (duration) {
74074                     _selection
74075                         .transition()
74076                         .duration(duration)
74077                         .on('start', function() { map.startEase(); })
74078                         .call(_zoomerPanner.transform, identity$2.translate(t[0], t[1]).scale(k));
74079                 } else {
74080                     projection.translate(t);
74081                     _transformStart = projection.transform();
74082                     _selection.call(_zoomerPanner.transform, _transformStart);
74083                     dispatch$1.call('move', this, map);
74084                     immediateRedraw();
74085                 }
74086
74087                 return map;
74088             };
74089
74090
74091             map.dimensions = function(val) {
74092                 if (!arguments.length) { return _dimensions; }
74093
74094                 _dimensions = val;
74095                 drawLayers.dimensions(_dimensions);
74096                 context.background().dimensions(_dimensions);
74097                 projection.clipExtent([[0, 0], _dimensions]);
74098                 _getMouseCoords = utilFastMouse(supersurface.node());
74099
74100                 scheduleRedraw();
74101                 return map;
74102             };
74103
74104
74105             function zoomIn(delta) {
74106                 setCenterZoom(map.center(), ~~map.zoom() + delta, 250, true);
74107             }
74108
74109             function zoomOut(delta) {
74110                 setCenterZoom(map.center(), ~~map.zoom() - delta, 250, true);
74111             }
74112
74113             map.zoomIn = function() { zoomIn(1); };
74114             map.zoomInFurther = function() { zoomIn(4); };
74115             map.canZoomIn = function() { return map.zoom() < maxZoom; };
74116
74117             map.zoomOut = function() { zoomOut(1); };
74118             map.zoomOutFurther = function() { zoomOut(4); };
74119             map.canZoomOut = function() { return map.zoom() > minZoom; };
74120
74121             map.center = function(loc2) {
74122                 if (!arguments.length) {
74123                     return projection.invert(pxCenter());
74124                 }
74125
74126                 if (setCenterZoom(loc2, map.zoom())) {
74127                     dispatch$1.call('move', this, map);
74128                 }
74129
74130                 scheduleRedraw();
74131                 return map;
74132             };
74133
74134             map.unobscuredCenterZoomEase = function(loc, zoom) {
74135                 var offset = map.unobscuredOffsetPx();
74136
74137                 var proj = geoRawMercator().transform(projection.transform());  // copy projection
74138                 // use the target zoom to calculate the offset center
74139                 proj.scale(geoZoomToScale(zoom, TILESIZE));
74140
74141                 var locPx = proj(loc);
74142                 var offsetLocPx = [locPx[0] + offset[0], locPx[1] + offset[1]];
74143                 var offsetLoc = proj.invert(offsetLocPx);
74144
74145                 map.centerZoomEase(offsetLoc, zoom);
74146             };
74147
74148             map.unobscuredOffsetPx = function() {
74149                 var openPane = context.container().select('.map-panes .map-pane.shown');
74150                 if (!openPane.empty()) {
74151                     return [openPane.node().offsetWidth/2, 0];
74152                 }
74153                 return [0, 0];
74154             };
74155
74156             map.zoom = function(z2) {
74157                 if (!arguments.length) {
74158                     return Math.max(geoScaleToZoom(projection.scale(), TILESIZE), 0);
74159                 }
74160
74161                 if (z2 < _minzoom) {
74162                     surface.interrupt();
74163                     dispatch$1.call('hitMinZoom', this, map);
74164                     z2 = context.minEditableZoom();
74165                 }
74166
74167                 if (setCenterZoom(map.center(), z2)) {
74168                     dispatch$1.call('move', this, map);
74169                 }
74170
74171                 scheduleRedraw();
74172                 return map;
74173             };
74174
74175
74176             map.centerZoom = function(loc2, z2) {
74177                 if (setCenterZoom(loc2, z2)) {
74178                     dispatch$1.call('move', this, map);
74179                 }
74180
74181                 scheduleRedraw();
74182                 return map;
74183             };
74184
74185
74186             map.zoomTo = function(entity) {
74187                 var extent = entity.extent(context.graph());
74188                 if (!isFinite(extent.area())) { return map; }
74189
74190                 var z2 = clamp(map.trimmedExtentZoom(extent), 0, 20);
74191                 return map.centerZoom(extent.center(), z2);
74192             };
74193
74194
74195             map.centerEase = function(loc2, duration) {
74196                 duration = duration || 250;
74197                 setCenterZoom(loc2, map.zoom(), duration);
74198                 return map;
74199             };
74200
74201
74202             map.zoomEase = function(z2, duration) {
74203                 duration = duration || 250;
74204                 setCenterZoom(map.center(), z2, duration, false);
74205                 return map;
74206             };
74207
74208
74209             map.centerZoomEase = function(loc2, z2, duration) {
74210                 duration = duration || 250;
74211                 setCenterZoom(loc2, z2, duration, false);
74212                 return map;
74213             };
74214
74215
74216             map.transformEase = function(t2, duration) {
74217                 duration = duration || 250;
74218                 setTransform(t2, duration, false /* don't force */);
74219                 return map;
74220             };
74221
74222
74223             map.zoomToEase = function(obj, duration) {
74224                 var extent;
74225                 if (Array.isArray(obj)) {
74226                     obj.forEach(function(entity) {
74227                         var entityExtent = entity.extent(context.graph());
74228                         if (!extent) {
74229                             extent = entityExtent;
74230                         } else {
74231                             extent = extent.extend(entityExtent);
74232                         }
74233                     });
74234                 } else {
74235                     extent = obj.extent(context.graph());
74236                 }
74237                 if (!isFinite(extent.area())) { return map; }
74238
74239                 var z2 = clamp(map.trimmedExtentZoom(extent), 0, 20);
74240                 return map.centerZoomEase(extent.center(), z2, duration);
74241             };
74242
74243
74244             map.startEase = function() {
74245                 utilBindOnce(surface, _pointerPrefix + 'down.ease', function() {
74246                     map.cancelEase();
74247                 });
74248                 return map;
74249             };
74250
74251
74252             map.cancelEase = function() {
74253                 _selection.interrupt();
74254                 return map;
74255             };
74256
74257
74258             map.extent = function(val) {
74259                 if (!arguments.length) {
74260                     return new geoExtent(
74261                         projection.invert([0, _dimensions[1]]),
74262                         projection.invert([_dimensions[0], 0])
74263                     );
74264                 } else {
74265                     var extent = geoExtent(val);
74266                     map.centerZoom(extent.center(), map.extentZoom(extent));
74267                 }
74268             };
74269
74270
74271             map.trimmedExtent = function(val) {
74272                 if (!arguments.length) {
74273                     var headerY = 71;
74274                     var footerY = 30;
74275                     var pad = 10;
74276                     return new geoExtent(
74277                         projection.invert([pad, _dimensions[1] - footerY - pad]),
74278                         projection.invert([_dimensions[0] - pad, headerY + pad])
74279                     );
74280                 } else {
74281                     var extent = geoExtent(val);
74282                     map.centerZoom(extent.center(), map.trimmedExtentZoom(extent));
74283                 }
74284             };
74285
74286
74287             function calcExtentZoom(extent, dim) {
74288                 var tl = projection([extent[0][0], extent[1][1]]);
74289                 var br = projection([extent[1][0], extent[0][1]]);
74290
74291                 // Calculate maximum zoom that fits extent
74292                 var hFactor = (br[0] - tl[0]) / dim[0];
74293                 var vFactor = (br[1] - tl[1]) / dim[1];
74294                 var hZoomDiff = Math.log(Math.abs(hFactor)) / Math.LN2;
74295                 var vZoomDiff = Math.log(Math.abs(vFactor)) / Math.LN2;
74296                 var newZoom = map.zoom() - Math.max(hZoomDiff, vZoomDiff);
74297
74298                 return newZoom;
74299             }
74300
74301
74302             map.extentZoom = function(val) {
74303                 return calcExtentZoom(geoExtent(val), _dimensions);
74304             };
74305
74306
74307             map.trimmedExtentZoom = function(val) {
74308                 var trimY = 120;
74309                 var trimX = 40;
74310                 var trimmed = [_dimensions[0] - trimX, _dimensions[1] - trimY];
74311                 return calcExtentZoom(geoExtent(val), trimmed);
74312             };
74313
74314
74315             map.withinEditableZoom = function() {
74316                 return map.zoom() >= context.minEditableZoom();
74317             };
74318
74319
74320             map.isInWideSelection = function() {
74321                 return !map.withinEditableZoom() && context.selectedIDs().length;
74322             };
74323
74324
74325             map.editableDataEnabled = function(skipZoomCheck) {
74326
74327                 var layer = context.layers().layer('osm');
74328                 if (!layer || !layer.enabled()) { return false; }
74329
74330                 return skipZoomCheck || map.withinEditableZoom();
74331             };
74332
74333
74334             map.notesEditable = function() {
74335                 var layer = context.layers().layer('notes');
74336                 if (!layer || !layer.enabled()) { return false; }
74337
74338                 return map.withinEditableZoom();
74339             };
74340
74341
74342             map.minzoom = function(val) {
74343                 if (!arguments.length) { return _minzoom; }
74344                 _minzoom = val;
74345                 return map;
74346             };
74347
74348
74349             map.toggleHighlightEdited = function() {
74350                 surface.classed('highlight-edited', !surface.classed('highlight-edited'));
74351                 map.pan([0,0]);  // trigger a redraw
74352                 dispatch$1.call('changeHighlighting', this);
74353             };
74354
74355
74356             map.areaFillOptions = ['wireframe', 'partial', 'full'];
74357
74358             map.activeAreaFill = function(val) {
74359                 if (!arguments.length) { return corePreferences('area-fill') || 'partial'; }
74360
74361                 corePreferences('area-fill', val);
74362                 if (val !== 'wireframe') {
74363                     corePreferences('area-fill-toggle', val);
74364                 }
74365                 updateAreaFill();
74366                 map.pan([0,0]);  // trigger a redraw
74367                 dispatch$1.call('changeAreaFill', this);
74368                 return map;
74369             };
74370
74371             map.toggleWireframe = function() {
74372
74373                 var activeFill = map.activeAreaFill();
74374
74375                 if (activeFill === 'wireframe') {
74376                     activeFill = corePreferences('area-fill-toggle') || 'partial';
74377                 } else {
74378                     activeFill = 'wireframe';
74379                 }
74380
74381                 map.activeAreaFill(activeFill);
74382             };
74383
74384             function updateAreaFill() {
74385                 var activeFill = map.activeAreaFill();
74386                 map.areaFillOptions.forEach(function(opt) {
74387                     surface.classed('fill-' + opt, Boolean(opt === activeFill));
74388                 });
74389             }
74390
74391
74392             map.layers = function () { return drawLayers; };
74393
74394
74395             map.doubleUpHandler = function() {
74396                 return _doubleUpHandler;
74397             };
74398
74399
74400             return utilRebind(map, dispatch$1, 'on');
74401         }
74402
74403         function rendererPhotos(context) {
74404             var dispatch$1 = dispatch('change');
74405             var _layerIDs = ['streetside', 'mapillary', 'mapillary-map-features', 'mapillary-signs', 'openstreetcam'];
74406             var _allPhotoTypes = ['flat', 'panoramic'];
74407             var _shownPhotoTypes = _allPhotoTypes.slice();   // shallow copy
74408
74409             function photos() {}
74410
74411             function updateStorage() {
74412                 if (window.mocha) { return; }
74413
74414                 var hash = utilStringQs(window.location.hash);
74415                 var enabled = context.layers().all().filter(function(d) {
74416                     return _layerIDs.indexOf(d.id) !== -1 && d.layer && d.layer.supported() && d.layer.enabled();
74417                 }).map(function(d) {
74418                     return d.id;
74419                 });
74420                 if (enabled.length) {
74421                     hash.photo_overlay = enabled.join(',');
74422                 } else {
74423                     delete hash.photo_overlay;
74424                 }
74425                 window.location.replace('#' + utilQsString(hash, true));
74426             }
74427
74428             photos.overlayLayerIDs = function() {
74429                 return _layerIDs;
74430             };
74431
74432             photos.allPhotoTypes = function() {
74433                 return _allPhotoTypes;
74434             };
74435
74436             function showsLayer(id) {
74437                 var layer = context.layers().layer(id);
74438                 return layer && layer.supported() && layer.enabled();
74439             }
74440
74441             photos.shouldFilterByPhotoType = function() {
74442                 return showsLayer('mapillary') ||
74443                     (showsLayer('streetside') && showsLayer('openstreetcam'));
74444             };
74445
74446             photos.showsPhotoType = function(val) {
74447                 if (!photos.shouldFilterByPhotoType()) { return true; }
74448
74449                 return _shownPhotoTypes.indexOf(val) !== -1;
74450             };
74451
74452             photos.showsFlat = function() {
74453                 return photos.showsPhotoType('flat');
74454             };
74455
74456             photos.showsPanoramic = function() {
74457                 return photos.showsPhotoType('panoramic');
74458             };
74459
74460             photos.togglePhotoType = function(val) {
74461                 var index = _shownPhotoTypes.indexOf(val);
74462                 if (index !== -1) {
74463                     _shownPhotoTypes.splice(index, 1);
74464                 } else {
74465                     _shownPhotoTypes.push(val);
74466                 }
74467                 dispatch$1.call('change', this);
74468                 return photos;
74469             };
74470
74471             photos.init = function() {
74472                 var hash = utilStringQs(window.location.hash);
74473                 if (hash.photo_overlay) {
74474                     var hashOverlayIDs = hash.photo_overlay.replace(/;/g, ',').split(',');
74475                     hashOverlayIDs.forEach(function(id) {
74476                         var layer = context.layers().layer(id);
74477                         if (layer) { layer.enabled(true); }
74478                     });
74479                 }
74480
74481                 context.layers().on('change.rendererPhotos', updateStorage);
74482             };
74483
74484             return utilRebind(photos, dispatch$1, 'on');
74485         }
74486
74487         function uiAccount(context) {
74488             var osm = context.connection();
74489
74490
74491             function update(selection) {
74492                 if (!osm) { return; }
74493
74494                 if (!osm.authenticated()) {
74495                     selection.selectAll('.userLink, .logoutLink')
74496                         .classed('hide', true);
74497                     return;
74498                 }
74499
74500                 osm.userDetails(function(err, details) {
74501                     var userLink = selection.select('.userLink'),
74502                         logoutLink = selection.select('.logoutLink');
74503
74504                     userLink.html('');
74505                     logoutLink.html('');
74506
74507                     if (err || !details) { return; }
74508
74509                     selection.selectAll('.userLink, .logoutLink')
74510                         .classed('hide', false);
74511
74512                     // Link
74513                     userLink.append('a')
74514                         .attr('href', osm.userURL(details.display_name))
74515                         .attr('target', '_blank');
74516
74517                     // Add thumbnail or dont
74518                     if (details.image_url) {
74519                         userLink.append('img')
74520                             .attr('class', 'icon pre-text user-icon')
74521                             .attr('src', details.image_url);
74522                     } else {
74523                         userLink
74524                             .call(svgIcon('#iD-icon-avatar', 'pre-text light'));
74525                     }
74526
74527                     // Add user name
74528                     userLink.append('span')
74529                         .attr('class', 'label')
74530                         .text(details.display_name);
74531
74532                     logoutLink.append('a')
74533                         .attr('class', 'logout')
74534                         .attr('href', '#')
74535                         .text(_t('logout'))
74536                         .on('click.logout', function() {
74537                             event.preventDefault();
74538                             osm.logout();
74539                         });
74540                 });
74541             }
74542
74543
74544             return function(selection) {
74545                 selection.append('li')
74546                     .attr('class', 'logoutLink')
74547                     .classed('hide', true);
74548
74549                 selection.append('li')
74550                     .attr('class', 'userLink')
74551                     .classed('hide', true);
74552
74553                 if (osm) {
74554                     osm.on('change.account', function() { update(selection); });
74555                     update(selection);
74556                 }
74557             };
74558         }
74559
74560         function uiAttribution(context) {
74561           var _selection = select(null);
74562
74563
74564           function render(selection, data, klass) {
74565             var div = selection.selectAll(("." + klass))
74566               .data([0]);
74567
74568             div = div.enter()
74569               .append('div')
74570               .attr('class', klass)
74571               .merge(div);
74572
74573
74574             var attributions = div.selectAll('.attribution')
74575               .data(data, function (d) { return d.id; });
74576
74577             attributions.exit()
74578               .remove();
74579
74580             attributions = attributions.enter()
74581               .append('span')
74582               .attr('class', 'attribution')
74583               .each(function (d, i, nodes) {
74584                 var attribution = select(nodes[i]);
74585
74586                 if (d.terms_html) {
74587                   attribution.html(d.terms_html);
74588                   return;
74589                 }
74590
74591                 if (d.terms_url) {
74592                   attribution = attribution
74593                     .append('a')
74594                     .attr('href', d.terms_url)
74595                     .attr('target', '_blank');
74596                 }
74597
74598                 var sourceID = d.id.replace(/\./g, '<TX_DOT>');
74599                 var terms_text = _t(("imagery." + sourceID + ".attribution.text"),
74600                   { default: d.terms_text || d.id || d.name() }
74601                 );
74602
74603                 if (d.icon && !d.overlay) {
74604                   attribution
74605                     .append('img')
74606                     .attr('class', 'source-image')
74607                     .attr('src', d.icon);
74608                 }
74609
74610                 attribution
74611                   .append('span')
74612                   .attr('class', 'attribution-text')
74613                   .text(terms_text);
74614               })
74615               .merge(attributions);
74616
74617
74618             var copyright = attributions.selectAll('.copyright-notice')
74619               .data(function (d) {
74620                 var notice = d.copyrightNotices(context.map().zoom(), context.map().extent());
74621                 return notice ? [notice] : [];
74622               });
74623
74624             copyright.exit()
74625               .remove();
74626
74627             copyright = copyright.enter()
74628               .append('span')
74629               .attr('class', 'copyright-notice')
74630               .merge(copyright);
74631
74632             copyright
74633               .text(String);
74634           }
74635
74636
74637           function update() {
74638             var baselayer = context.background().baseLayerSource();
74639             _selection
74640               .call(render, (baselayer ? [baselayer] : []), 'base-layer-attribution');
74641
74642             var z = context.map().zoom();
74643             var overlays = context.background().overlayLayerSources() || [];
74644             _selection
74645               .call(render, overlays.filter(function (s) { return s.validZoom(z); }), 'overlay-layer-attribution');
74646           }
74647
74648
74649           return function(selection) {
74650             _selection = selection;
74651
74652             context.background()
74653               .on('change.attribution', update);
74654
74655             context.map()
74656               .on('move.attribution', throttle(update, 400, { leading: false }));
74657
74658             update();
74659           };
74660         }
74661
74662         function uiContributors(context) {
74663             var osm = context.connection(),
74664                 debouncedUpdate = debounce(function() { update(); }, 1000),
74665                 limit = 4,
74666                 hidden = false,
74667                 wrap = select(null);
74668
74669
74670             function update() {
74671                 if (!osm) { return; }
74672
74673                 var users = {},
74674                     entities = context.history().intersects(context.map().extent());
74675
74676                 entities.forEach(function(entity) {
74677                     if (entity && entity.user) { users[entity.user] = true; }
74678                 });
74679
74680                 var u = Object.keys(users),
74681                     subset = u.slice(0, u.length > limit ? limit - 1 : limit);
74682
74683                 wrap.html('')
74684                     .call(svgIcon('#iD-icon-nearby', 'pre-text light'));
74685
74686                 var userList = select(document.createElement('span'));
74687
74688                 userList.selectAll()
74689                     .data(subset)
74690                     .enter()
74691                     .append('a')
74692                     .attr('class', 'user-link')
74693                     .attr('href', function(d) { return osm.userURL(d); })
74694                     .attr('target', '_blank')
74695                     .text(String);
74696
74697                 if (u.length > limit) {
74698                     var count = select(document.createElement('span'));
74699
74700                     count.append('a')
74701                         .attr('target', '_blank')
74702                         .attr('href', function() {
74703                             return osm.changesetsURL(context.map().center(), context.map().zoom());
74704                         })
74705                         .text(u.length - limit + 1);
74706
74707                     wrap.append('span')
74708                         .html(_t('contributors.truncated_list', { users: userList.html(), count: count.html() }));
74709
74710                 } else {
74711                     wrap.append('span')
74712                         .html(_t('contributors.list', { users: userList.html() }));
74713                 }
74714
74715                 if (!u.length) {
74716                     hidden = true;
74717                     wrap
74718                         .transition()
74719                         .style('opacity', 0);
74720
74721                 } else if (hidden) {
74722                     wrap
74723                         .transition()
74724                         .style('opacity', 1);
74725                 }
74726             }
74727
74728
74729             return function(selection) {
74730                 if (!osm) { return; }
74731                 wrap = selection;
74732                 update();
74733
74734                 osm.on('loaded.contributors', debouncedUpdate);
74735                 context.map().on('move.contributors', debouncedUpdate);
74736             };
74737         }
74738
74739         var _popoverID = 0;
74740
74741         function uiPopover(klass) {
74742             var _id = _popoverID++;
74743             var _anchorSelection = select(null);
74744             var popover = function(selection) {
74745                 _anchorSelection = selection;
74746                 selection.each(setup);
74747             };
74748             var _animation = utilFunctor(false);
74749             var _placement = utilFunctor('top'); // top, bottom, left, right
74750             var _alignment = utilFunctor('center');  // leading, center, trailing
74751             var _scrollContainer = utilFunctor(select(null));
74752             var _content;
74753             var _displayType = utilFunctor('');
74754             var _hasArrow = utilFunctor(true);
74755
74756             // use pointer events on supported platforms; fallback to mouse events
74757             var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
74758
74759             popover.displayType = function(val) {
74760                 if (arguments.length) {
74761                     _displayType = utilFunctor(val);
74762                     return popover;
74763                 } else {
74764                     return _displayType;
74765                 }
74766             };
74767
74768             popover.hasArrow = function(val) {
74769                 if (arguments.length) {
74770                     _hasArrow = utilFunctor(val);
74771                     return popover;
74772                 } else {
74773                     return _hasArrow;
74774                 }
74775             };
74776
74777             popover.placement = function(val) {
74778                 if (arguments.length) {
74779                     _placement = utilFunctor(val);
74780                     return popover;
74781                 } else {
74782                     return _placement;
74783                 }
74784             };
74785
74786             popover.alignment = function(val) {
74787                 if (arguments.length) {
74788                     _alignment = utilFunctor(val);
74789                     return popover;
74790                 } else {
74791                     return _alignment;
74792                 }
74793             };
74794
74795             popover.scrollContainer = function(val) {
74796                 if (arguments.length) {
74797                     _scrollContainer = utilFunctor(val);
74798                     return popover;
74799                 } else {
74800                     return _scrollContainer;
74801                 }
74802             };
74803
74804             popover.content = function(val) {
74805                 if (arguments.length) {
74806                     _content = val;
74807                     return popover;
74808                 } else {
74809                     return _content;
74810                 }
74811             };
74812
74813             popover.isShown = function() {
74814                 var popoverSelection = _anchorSelection.select('.popover-' + _id);
74815                 return !popoverSelection.empty() && popoverSelection.classed('in');
74816             };
74817
74818             popover.show = function() {
74819                 _anchorSelection.each(show);
74820             };
74821
74822             popover.updateContent = function() {
74823                 _anchorSelection.each(updateContent);
74824             };
74825
74826             popover.hide = function() {
74827                 _anchorSelection.each(hide);
74828             };
74829
74830             popover.toggle = function() {
74831                 _anchorSelection.each(toggle);
74832             };
74833
74834             popover.destroy = function(selection, selector) {
74835                 // by default, just destroy the current popover
74836                 selector = selector || '.popover-' + _id;
74837
74838                 selection
74839                     .on(_pointerPrefix + 'enter.popover', null)
74840                     .on(_pointerPrefix + 'leave.popover', null)
74841                     .on(_pointerPrefix + 'up.popover', null)
74842                     .on(_pointerPrefix + 'down.popover', null)
74843                     .on('click.popover', null)
74844                     .attr('title', function() {
74845                         return this.getAttribute('data-original-title') || this.getAttribute('title');
74846                     })
74847                     .attr('data-original-title', null)
74848                     .selectAll(selector)
74849                     .remove();
74850             };
74851
74852
74853             popover.destroyAny = function(selection) {
74854                 selection.call(popover.destroy, '.popover');
74855             };
74856
74857             function setup() {
74858                 var anchor = select(this);
74859                 var animate = _animation.apply(this, arguments);
74860                 var popoverSelection = anchor.selectAll('.popover-' + _id)
74861                     .data([0]);
74862
74863
74864                 var enter = popoverSelection.enter()
74865                     .append('div')
74866                     .attr('class', 'popover popover-' + _id + ' ' + (klass ? klass : ''))
74867                     .classed('arrowed', _hasArrow.apply(this, arguments));
74868
74869                 enter
74870                     .append('div')
74871                     .attr('class', 'popover-arrow');
74872
74873                 enter
74874                     .append('div')
74875                     .attr('class', 'popover-inner');
74876
74877                 popoverSelection = enter
74878                     .merge(popoverSelection);
74879
74880                 if (animate) {
74881                     popoverSelection.classed('fade', true);
74882                 }
74883
74884                 var display = _displayType.apply(this, arguments);
74885
74886                 if (display === 'hover') {
74887                     var _lastNonMouseEnterTime;
74888                     anchor.on(_pointerPrefix + 'enter.popover', function() {
74889
74890                         if (event.pointerType) {
74891                             if (event.pointerType !== 'mouse') {
74892                                 _lastNonMouseEnterTime = event.timeStamp;
74893                                 // only allow hover behavior for mouse input
74894                                 return;
74895                             } else if (_lastNonMouseEnterTime &&
74896                                 event.timeStamp - _lastNonMouseEnterTime < 1500) {
74897                                 // HACK: iOS 13.4 sends an erroneous `mouse` type pointerenter
74898                                 // event for non-mouse interactions right after sending
74899                                 // the correct type pointerenter event. Workaround by discarding
74900                                 // any mouse event that occurs immediately after a non-mouse event.
74901                                 return;
74902                             }
74903                         }
74904
74905                         // don't show if buttons are pressed, e.g. during click and drag of map
74906                         if (event.buttons !== 0) { return; }
74907
74908                         show.apply(this, arguments);
74909                     });
74910                     anchor.on(_pointerPrefix + 'leave.popover', function() {
74911                         hide.apply(this, arguments);
74912                     });
74913
74914                 } else if (display === 'clickFocus') {
74915                     anchor
74916                         .on(_pointerPrefix + 'down.popover', function() {
74917                             event.preventDefault();
74918                             event.stopPropagation();
74919                         })
74920                         .on(_pointerPrefix + 'up.popover', function() {
74921                             event.preventDefault();
74922                             event.stopPropagation();
74923                         })
74924                         .on('click.popover', toggle);
74925
74926                     popoverSelection
74927                         .attr('tabindex', 0)
74928                         .on('blur.popover', function() {
74929                             anchor.each(function() {
74930                                 hide.apply(this, arguments);
74931                             });
74932                         });
74933                 }
74934             }
74935
74936
74937             function show() {
74938                 var anchor = select(this);
74939                 var popoverSelection = anchor.selectAll('.popover-' + _id);
74940
74941                 if (popoverSelection.empty()) {
74942                     // popover was removed somehow, put it back
74943                     anchor.call(popover.destroy);
74944                     anchor.each(setup);
74945                     popoverSelection = anchor.selectAll('.popover-' + _id);
74946                 }
74947
74948                 popoverSelection.classed('in', true);
74949
74950                 var displayType = _displayType.apply(this, arguments);
74951                 if (displayType === 'clickFocus') {
74952                     anchor.classed('active', true);
74953                     popoverSelection.node().focus();
74954                 }
74955
74956                 anchor.each(updateContent);
74957             }
74958
74959             function updateContent() {
74960                 var anchor = select(this);
74961
74962                 if (_content) {
74963                     anchor.selectAll('.popover-' + _id + ' > .popover-inner')
74964                         .call(_content.apply(this, arguments));
74965                 }
74966
74967                 updatePosition.apply(this, arguments);
74968                 // hack: update multiple times to fix instances where the absolute offset is
74969                 // set before the dynamic popover size is calculated by the browser
74970                 updatePosition.apply(this, arguments);
74971                 updatePosition.apply(this, arguments);
74972             }
74973
74974
74975             function updatePosition() {
74976
74977                 var anchor = select(this);
74978                 var popoverSelection = anchor.selectAll('.popover-' + _id);
74979
74980                 var scrollContainer = _scrollContainer && _scrollContainer.apply(this, arguments);
74981                 var scrollNode = scrollContainer && !scrollContainer.empty() && scrollContainer.node();
74982                 var scrollLeft = scrollNode ? scrollNode.scrollLeft : 0;
74983                 var scrollTop = scrollNode ? scrollNode.scrollTop : 0;
74984
74985                 var placement = _placement.apply(this, arguments);
74986                 popoverSelection
74987                     .classed('left', false)
74988                     .classed('right', false)
74989                     .classed('top', false)
74990                     .classed('bottom', false)
74991                     .classed(placement, true);
74992
74993                 var alignment = _alignment.apply(this, arguments);
74994                 var alignFactor = 0.5;
74995                 if (alignment === 'leading') {
74996                     alignFactor = 0;
74997                 } else if (alignment === 'trailing') {
74998                     alignFactor = 1;
74999                 }
75000                 var anchorFrame = getFrame(anchor.node());
75001                 var popoverFrame = getFrame(popoverSelection.node());
75002                 var position;
75003
75004                 switch (placement) {
75005                     case 'top':
75006                     position = {
75007                         x: anchorFrame.x + (anchorFrame.w - popoverFrame.w) * alignFactor,
75008                         y: anchorFrame.y - popoverFrame.h
75009                     };
75010                     break;
75011                     case 'bottom':
75012                     position = {
75013                         x: anchorFrame.x + (anchorFrame.w - popoverFrame.w) * alignFactor,
75014                         y: anchorFrame.y + anchorFrame.h
75015                     };
75016                     break;
75017                     case 'left':
75018                     position = {
75019                         x: anchorFrame.x - popoverFrame.w,
75020                         y: anchorFrame.y + (anchorFrame.h - popoverFrame.h) * alignFactor
75021                     };
75022                     break;
75023                     case 'right':
75024                     position = {
75025                         x: anchorFrame.x + anchorFrame.w,
75026                         y: anchorFrame.y + (anchorFrame.h - popoverFrame.h) * alignFactor
75027                     };
75028                     break;
75029                 }
75030
75031                 if (position) {
75032
75033                     if (scrollNode && (placement === 'top' || placement === 'bottom')) {
75034
75035                         var initialPosX = position.x;
75036
75037                         if (position.x + popoverFrame.w > scrollNode.offsetWidth - 10) {
75038                             position.x = scrollNode.offsetWidth - 10 - popoverFrame.w;
75039                         } else if (position.x < 10) {
75040                             position.x = 10;
75041                         }
75042
75043                         var arrow = anchor.selectAll('.popover-' + _id + ' > .popover-arrow');
75044                         // keep the arrow centered on the button, or as close as possible
75045                         var arrowPosX = Math.min(Math.max(popoverFrame.w / 2 - (position.x - initialPosX), 10), popoverFrame.w - 10);
75046                         arrow.style('left', ~~arrowPosX + 'px');
75047                     }
75048
75049                     popoverSelection.style('left', ~~position.x + 'px').style('top', ~~position.y + 'px');
75050                 } else {
75051                     popoverSelection.style('left', null).style('top', null);
75052                 }
75053
75054                 function getFrame(node) {
75055                     var positionStyle = select(node).style('position');
75056                     if (positionStyle === 'absolute' || positionStyle === 'static') {
75057                         return {
75058                             x: node.offsetLeft - scrollLeft,
75059                             y: node.offsetTop - scrollTop,
75060                             w: node.offsetWidth,
75061                             h: node.offsetHeight
75062                         };
75063                     } else {
75064                         return {
75065                             x: 0,
75066                             y: 0,
75067                             w: node.offsetWidth,
75068                             h: node.offsetHeight
75069                         };
75070                     }
75071                 }
75072             }
75073
75074
75075             function hide() {
75076                 var anchor = select(this);
75077                 if (_displayType.apply(this, arguments) === 'clickFocus') {
75078                     anchor.classed('active', false);
75079                 }
75080                 anchor.selectAll('.popover-' + _id).classed('in', false);
75081             }
75082
75083
75084             function toggle() {
75085                 if (select(this).select('.popover-' + _id).classed('in')) {
75086                     hide.apply(this, arguments);
75087                 } else {
75088                     show.apply(this, arguments);
75089                 }
75090             }
75091
75092
75093             return popover;
75094         }
75095
75096         function uiTooltip(klass) {
75097
75098             var tooltip = uiPopover((klass || '') + ' tooltip')
75099                 .displayType('hover');
75100
75101             var _title = function() {
75102                 var title = this.getAttribute('data-original-title');
75103                 if (title) {
75104                     return title;
75105                 } else {
75106                     title = this.getAttribute('title');
75107                     this.removeAttribute('title');
75108                     this.setAttribute('data-original-title', title);
75109                 }
75110                 return title;
75111             };
75112
75113             var _heading = utilFunctor(null);
75114             var _keys = utilFunctor(null);
75115
75116             tooltip.title = function(val) {
75117                 if (!arguments.length) { return _title; }
75118                 _title = utilFunctor(val);
75119                 return tooltip;
75120             };
75121
75122             tooltip.heading = function(val) {
75123                 if (!arguments.length) { return _heading; }
75124                 _heading = utilFunctor(val);
75125                 return tooltip;
75126             };
75127
75128             tooltip.keys = function(val) {
75129                 if (!arguments.length) { return _keys; }
75130                 _keys = utilFunctor(val);
75131                 return tooltip;
75132             };
75133
75134             tooltip.content(function() {
75135                 var heading = _heading.apply(this, arguments);
75136                 var text = _title.apply(this, arguments);
75137                 var keys = _keys.apply(this, arguments);
75138
75139                 return function(selection) {
75140
75141                     var headingSelect = selection
75142                         .selectAll('.tooltip-heading')
75143                         .data(heading ? [heading] :[]);
75144
75145                     headingSelect.exit()
75146                         .remove();
75147
75148                     headingSelect.enter()
75149                         .append('div')
75150                         .attr('class', 'tooltip-heading')
75151                         .merge(headingSelect)
75152                         .html(heading);
75153
75154                     var textSelect = selection
75155                         .selectAll('.tooltip-text')
75156                         .data(text ? [text] :[]);
75157
75158                     textSelect.exit()
75159                         .remove();
75160
75161                     textSelect.enter()
75162                         .append('div')
75163                         .attr('class', 'tooltip-text')
75164                         .merge(textSelect)
75165                         .html(text);
75166
75167                     var keyhintWrap = selection
75168                         .selectAll('.keyhint-wrap')
75169                         .data(keys && keys.length ? [0] : []);
75170
75171                     keyhintWrap.exit()
75172                         .remove();
75173
75174                     var keyhintWrapEnter = keyhintWrap.enter()
75175                         .append('div')
75176                         .attr('class', 'keyhint-wrap');
75177
75178                     keyhintWrapEnter
75179                         .append('span')
75180                         .html(_t('tooltip_keyhint'));
75181
75182                     keyhintWrap = keyhintWrapEnter.merge(keyhintWrap);
75183
75184                     keyhintWrap.selectAll('kbd.shortcut')
75185                         .data(keys && keys.length ? keys : [])
75186                         .enter()
75187                         .append('kbd')
75188                         .attr('class', 'shortcut')
75189                         .html(function(d) {
75190                             return d;
75191                         });
75192                 };
75193             });
75194
75195             return tooltip;
75196         }
75197
75198         function uiEditMenu(context) {
75199             var dispatch$1 = dispatch('toggled');
75200
75201             var _menu = select(null);
75202             var _operations = [];
75203             // the position the menu should be displayed relative to
75204             var _anchorLoc = [0, 0];
75205             var _anchorLocLonLat = [0, 0];
75206             // a string indicating how the menu was opened
75207             var _triggerType = '';
75208
75209             var _vpTopMargin = 85; // viewport top margin
75210             var _vpBottomMargin = 45; // viewport bottom margin
75211             var _vpSideMargin = 35;   // viewport side margin
75212
75213             var _menuTop = false;
75214             var _menuHeight;
75215             var _menuWidth;
75216
75217             // hardcode these values to make menu positioning easier
75218             var _verticalPadding = 4;
75219
75220             // see also `.edit-menu .tooltip` CSS; include margin
75221             var _tooltipWidth = 210;
75222
75223             // offset the menu slightly from the target location
75224             var _menuSideMargin = 10;
75225
75226             var _tooltips = [];
75227
75228             var editMenu = function(selection) {
75229
75230                 var isTouchMenu = _triggerType.includes('touch') || _triggerType.includes('pen');
75231
75232                 var ops = _operations.filter(function(op) {
75233                     return !isTouchMenu || !op.mouseOnly;
75234                 });
75235
75236                 if (!ops.length) { return; }
75237
75238                 _tooltips = [];
75239
75240                 // Position the menu above the anchor for stylus and finger input
75241                 // since the mapper's hand likely obscures the screen below the anchor
75242                 _menuTop = isTouchMenu;
75243
75244                 // Show labels for touch input since there aren't hover tooltips
75245                 var showLabels = isTouchMenu;
75246
75247                 var buttonHeight = showLabels ? 32 : 34;
75248                 if (showLabels) {
75249                     // Get a general idea of the width based on the length of the label
75250                     _menuWidth = 52 + Math.min(120, 6 * Math.max.apply(Math, ops.map(function(op) {
75251                         return op.title.length;
75252                     })));
75253                 } else {
75254                     _menuWidth = 44;
75255                 }
75256
75257                 _menuHeight = _verticalPadding * 2 + ops.length * buttonHeight;
75258
75259                 _menu = selection
75260                     .append('div')
75261                     .attr('class', 'edit-menu')
75262                     .classed('touch-menu', isTouchMenu)
75263                     .style('padding', _verticalPadding + 'px 0');
75264
75265                 var buttons = _menu.selectAll('.edit-menu-item')
75266                     .data(ops);
75267
75268                 // enter
75269                 var buttonsEnter = buttons.enter()
75270                     .append('button')
75271                     .attr('class', function (d) { return 'edit-menu-item edit-menu-item-' + d.id; })
75272                     .style('height', buttonHeight + 'px')
75273                     .on('click', click)
75274                     // don't listen for `mouseup` because we only care about non-mouse pointer types
75275                     .on('pointerup', pointerup)
75276                     .on('pointerdown mousedown', function pointerdown() {
75277                         // don't let button presses also act as map input - #1869
75278                         event.stopPropagation();
75279                     });
75280
75281                 buttonsEnter.each(function(d) {
75282                     var tooltip = uiTooltip()
75283                         .heading(d.title)
75284                         .title(d.tooltip())
75285                         .keys([d.keys[0]]);
75286
75287                     _tooltips.push(tooltip);
75288
75289                     select(this)
75290                         .call(tooltip)
75291                         .append('div')
75292                         .attr('class', 'icon-wrap')
75293                         .call(svgIcon('#iD-operation-' + d.id, 'operation'));
75294                 });
75295
75296                 if (showLabels) {
75297                     buttonsEnter.append('span')
75298                         .attr('class', 'label')
75299                         .text(function(d) {
75300                             return d.title;
75301                         });
75302                 }
75303
75304                 // update
75305                 buttons = buttonsEnter
75306                     .merge(buttons)
75307                     .classed('disabled', function(d) { return d.disabled(); });
75308
75309                 updatePosition();
75310
75311                 var initialScale = context.projection.scale();
75312                 context.map()
75313                     .on('move.edit-menu', function() {
75314                         if (initialScale !== context.projection.scale()) {
75315                             editMenu.close();
75316                         }
75317                     })
75318                     .on('drawn.edit-menu', function(info) {
75319                         if (info.full) { updatePosition(); }
75320                     });
75321
75322                 var lastPointerUpType;
75323                 // `pointerup` is always called before `click`
75324                 function pointerup() {
75325                     lastPointerUpType = event.pointerType;
75326                 }
75327
75328                 function click(operation) {
75329                     event.stopPropagation();
75330                     if (operation.disabled()) {
75331                         if (lastPointerUpType === 'touch' ||
75332                             lastPointerUpType === 'pen') {
75333                             // there are no tooltips for touch interactions so flash feedback instead
75334                             context.ui().flash
75335                                 .duration(4000)
75336                                 .iconName('#iD-operation-' + operation.id)
75337                                 .iconClass('operation disabled')
75338                                 .text(operation.tooltip)();
75339                         }
75340                     } else {
75341                         if (lastPointerUpType === 'touch' ||
75342                             lastPointerUpType === 'pen') {
75343                             context.ui().flash
75344                                 .duration(2000)
75345                                 .iconName('#iD-operation-' + operation.id)
75346                                 .iconClass('operation')
75347                                 .text(operation.annotation() || operation.title)();
75348                         }
75349
75350                         operation();
75351                         editMenu.close();
75352                     }
75353                     lastPointerUpType = null;
75354                 }
75355
75356                 dispatch$1.call('toggled', this, true);
75357             };
75358
75359             function updatePosition() {
75360
75361                 if (!_menu || _menu.empty()) { return; }
75362
75363                 var anchorLoc = context.projection(_anchorLocLonLat);
75364
75365                 var viewport = context.surfaceRect();
75366
75367                 if (anchorLoc[0] < 0 ||
75368                     anchorLoc[0] > viewport.width ||
75369                     anchorLoc[1] < 0 ||
75370                     anchorLoc[1] > viewport.height) {
75371                     // close the menu if it's gone offscreen
75372
75373                     editMenu.close();
75374                     return;
75375                 }
75376
75377                 var menuLeft = displayOnLeft(viewport);
75378
75379                 var offset = [0, 0];
75380
75381                 offset[0] = menuLeft ? -1 * (_menuSideMargin + _menuWidth) : _menuSideMargin;
75382
75383                 if (_menuTop) {
75384                     if (anchorLoc[1] - _menuHeight < _vpTopMargin) {
75385                         // menu is near top viewport edge, shift downward
75386                         offset[1] = -anchorLoc[1] + _vpTopMargin;
75387                     } else {
75388                         offset[1] = -_menuHeight;
75389                     }
75390                 } else {
75391                     if (anchorLoc[1] + _menuHeight > (viewport.height - _vpBottomMargin)) {
75392                         // menu is near bottom viewport edge, shift upwards
75393                         offset[1] = -anchorLoc[1] - _menuHeight + viewport.height - _vpBottomMargin;
75394                     } else {
75395                         offset[1] = 0;
75396                     }
75397                 }
75398
75399                 var origin = geoVecAdd(anchorLoc, offset);
75400
75401                 _menu
75402                     .style('left', origin[0] + 'px')
75403                     .style('top', origin[1] + 'px');
75404
75405                 var tooltipSide = tooltipPosition(viewport, menuLeft);
75406                 _tooltips.forEach(function(tooltip) {
75407                     tooltip.placement(tooltipSide);
75408                 });
75409
75410                 function displayOnLeft(viewport) {
75411                     if (_mainLocalizer.textDirection() === 'ltr') {
75412                         if ((anchorLoc[0] + _menuSideMargin + _menuWidth) > (viewport.width - _vpSideMargin)) {
75413                             // right menu would be too close to the right viewport edge, go left
75414                             return true;
75415                         }
75416                         // prefer right menu
75417                         return false;
75418
75419                     } else { // rtl
75420                         if ((anchorLoc[0] - _menuSideMargin - _menuWidth) < _vpSideMargin) {
75421                             // left menu would be too close to the left viewport edge, go right
75422                             return false;
75423                         }
75424                         // prefer left menu
75425                         return true;
75426                     }
75427                 }
75428
75429                 function tooltipPosition(viewport, menuLeft) {
75430                     if (_mainLocalizer.textDirection() === 'ltr') {
75431                         if (menuLeft) {
75432                             // if there's not room for a right-side menu then there definitely
75433                             // isn't room for right-side tooltips
75434                             return 'left';
75435                         }
75436                         if ((anchorLoc[0] + _menuSideMargin + _menuWidth + _tooltipWidth) > (viewport.width - _vpSideMargin)) {
75437                             // right tooltips would be too close to the right viewport edge, go left
75438                             return 'left';
75439                         }
75440                         // prefer right tooltips
75441                         return 'right';
75442
75443                     } else { // rtl
75444                         if (!menuLeft) {
75445                             return 'right';
75446                         }
75447                         if ((anchorLoc[0] - _menuSideMargin - _menuWidth - _tooltipWidth) < _vpSideMargin) {
75448                             // left tooltips would be too close to the left viewport edge, go right
75449                             return 'right';
75450                         }
75451                         // prefer left tooltips
75452                         return 'left';
75453                     }
75454                 }
75455             }
75456
75457             editMenu.close = function () {
75458
75459                 context.map()
75460                     .on('move.edit-menu', null)
75461                     .on('drawn.edit-menu', null);
75462
75463                 _menu.remove();
75464                 _tooltips = [];
75465
75466                 dispatch$1.call('toggled', this, false);
75467             };
75468
75469             editMenu.anchorLoc = function(val) {
75470                 if (!arguments.length) { return _anchorLoc; }
75471                 _anchorLoc = val;
75472                 _anchorLocLonLat = context.projection.invert(_anchorLoc);
75473                 return editMenu;
75474             };
75475
75476             editMenu.triggerType = function(val) {
75477                 if (!arguments.length) { return _triggerType; }
75478                 _triggerType = val;
75479                 return editMenu;
75480             };
75481
75482             editMenu.operations = function(val) {
75483                 if (!arguments.length) { return _operations; }
75484                 _operations = val;
75485                 return editMenu;
75486             };
75487
75488             return utilRebind(editMenu, dispatch$1, 'on');
75489         }
75490
75491         function uiFeatureInfo(context) {
75492             function update(selection) {
75493                 var features = context.features();
75494                 var stats = features.stats();
75495                 var count = 0;
75496                 var hiddenList = features.hidden().map(function(k) {
75497                     if (stats[k]) {
75498                         count += stats[k];
75499                         return String(stats[k]) + ' ' + _t('feature.' + k + '.description');
75500                     }
75501                 }).filter(Boolean);
75502
75503                 selection.html('');
75504
75505                 if (hiddenList.length) {
75506                     var tooltipBehavior = uiTooltip()
75507                         .placement('top')
75508                         .title(function() {
75509                             return hiddenList.join('<br/>');
75510                         });
75511
75512                     selection.append('a')
75513                         .attr('class', 'chip')
75514                         .attr('href', '#')
75515                         .attr('tabindex', -1)
75516                         .html(_t('feature_info.hidden_warning', { count: count }))
75517                         .call(tooltipBehavior)
75518                         .on('click', function() {
75519                             tooltipBehavior.hide();
75520                             event.preventDefault();
75521                             // open the Map Data pane
75522                             context.ui().togglePanes(context.container().select('.map-panes .map-data-pane'));
75523                         });
75524                 }
75525
75526                 selection
75527                     .classed('hide', !hiddenList.length);
75528             }
75529
75530
75531             return function(selection) {
75532                 update(selection);
75533
75534                 context.features().on('change.feature_info', function() {
75535                     update(selection);
75536                 });
75537             };
75538         }
75539
75540         function uiFlash(context) {
75541             var _flashTimer;
75542
75543             var _duration = 2000;
75544             var _iconName = '#iD-icon-no';
75545             var _iconClass = 'disabled';
75546             var _text = '';
75547             var _textClass;
75548
75549             function flash() {
75550                 if (_flashTimer) {
75551                     _flashTimer.stop();
75552                 }
75553
75554                 context.container().select('.main-footer-wrap')
75555                     .classed('footer-hide', true)
75556                     .classed('footer-show', false);
75557                 context.container().select('.flash-wrap')
75558                     .classed('footer-hide', false)
75559                     .classed('footer-show', true);
75560
75561                 var content = context.container().select('.flash-wrap').selectAll('.flash-content')
75562                     .data([0]);
75563
75564                 // Enter
75565                 var contentEnter = content.enter()
75566                     .append('div')
75567                     .attr('class', 'flash-content');
75568
75569                 var iconEnter = contentEnter
75570                     .append('svg')
75571                     .attr('class', 'flash-icon icon')
75572                     .append('g')
75573                     .attr('transform', 'translate(10,10)');
75574
75575                 iconEnter
75576                     .append('circle')
75577                     .attr('r', 9);
75578
75579                 iconEnter
75580                     .append('use')
75581                     .attr('transform', 'translate(-7,-7)')
75582                     .attr('width', '14')
75583                     .attr('height', '14');
75584
75585                 contentEnter
75586                     .append('div')
75587                     .attr('class', 'flash-text');
75588
75589
75590                 // Update
75591                 content = content
75592                     .merge(contentEnter);
75593
75594                 content
75595                     .selectAll('.flash-icon')
75596                     .attr('class', 'icon flash-icon ' + (_iconClass || ''));
75597
75598                 content
75599                     .selectAll('.flash-icon use')
75600                     .attr('xlink:href', _iconName);
75601
75602                 content
75603                     .selectAll('.flash-text')
75604                     .attr('class', 'flash-text ' + (_textClass || ''))
75605                     .text(_text);
75606
75607
75608                 _flashTimer = d3_timeout(function() {
75609                     _flashTimer = null;
75610                     context.container().select('.main-footer-wrap')
75611                         .classed('footer-hide', false)
75612                         .classed('footer-show', true);
75613                     context.container().select('.flash-wrap')
75614                         .classed('footer-hide', true)
75615                         .classed('footer-show', false);
75616                 }, _duration);
75617
75618                 return content;
75619             }
75620
75621
75622             flash.duration = function(_) {
75623                 if (!arguments.length) { return _duration; }
75624                 _duration = _;
75625                 return flash;
75626             };
75627
75628             flash.text = function(_) {
75629                 if (!arguments.length) { return _text; }
75630                 _text = _;
75631                 return flash;
75632             };
75633
75634             flash.textClass = function(_) {
75635                 if (!arguments.length) { return _textClass; }
75636                 _textClass = _;
75637                 return flash;
75638             };
75639
75640             flash.iconName = function(_) {
75641                 if (!arguments.length) { return _iconName; }
75642                 _iconName = _;
75643                 return flash;
75644             };
75645
75646             flash.iconClass = function(_) {
75647                 if (!arguments.length) { return _iconClass; }
75648                 _iconClass = _;
75649                 return flash;
75650             };
75651
75652             return flash;
75653         }
75654
75655         function uiFullScreen(context) {
75656             var element = context.container().node();
75657             // var button = d3_select(null);
75658
75659
75660             function getFullScreenFn() {
75661                 if (element.requestFullscreen) {
75662                     return element.requestFullscreen;
75663                 } else if (element.msRequestFullscreen) {
75664                     return element.msRequestFullscreen;
75665                 } else if (element.mozRequestFullScreen) {
75666                     return element.mozRequestFullScreen;
75667                 } else if (element.webkitRequestFullscreen) {
75668                     return element.webkitRequestFullscreen;
75669                 }
75670             }
75671
75672
75673             function getExitFullScreenFn() {
75674                 if (document.exitFullscreen) {
75675                     return document.exitFullscreen;
75676                 } else if (document.msExitFullscreen) {
75677                     return document.msExitFullscreen;
75678                 } else if (document.mozCancelFullScreen) {
75679                     return document.mozCancelFullScreen;
75680                 } else if (document.webkitExitFullscreen) {
75681                     return document.webkitExitFullscreen;
75682                 }
75683             }
75684
75685
75686             function isFullScreen() {
75687                 return document.fullscreenElement ||
75688                     document.mozFullScreenElement ||
75689                     document.webkitFullscreenElement ||
75690                     document.msFullscreenElement;
75691             }
75692
75693
75694             function isSupported() {
75695                 return !!getFullScreenFn();
75696             }
75697
75698
75699             function fullScreen() {
75700                 event.preventDefault();
75701                 if (!isFullScreen()) {
75702                     // button.classed('active', true);
75703                     getFullScreenFn().apply(element);
75704                 } else {
75705                     // button.classed('active', false);
75706                     getExitFullScreenFn().apply(document);
75707                 }
75708             }
75709
75710
75711             return function() { // selection) {
75712                 if (!isSupported()) { return; }
75713
75714                 // button = selection.append('button')
75715                 //     .attr('title', t('full_screen'))
75716                 //     .attr('tabindex', -1)
75717                 //     .on('click', fullScreen)
75718                 //     .call(tooltip);
75719
75720                 // button.append('span')
75721                 //     .attr('class', 'icon full-screen');
75722
75723                 var detected = utilDetect();
75724                 var keys = (detected.os === 'mac' ? [uiCmd('⌃⌘F'), 'f11'] : ['f11']);
75725                 context.keybinding().on(keys, fullScreen);
75726             };
75727         }
75728
75729         function uiGeolocate(context) {
75730             var _geolocationOptions = {
75731                 // prioritize speed and power usage over precision
75732                 enableHighAccuracy: false,
75733                 // don't hang indefinitely getting the location
75734                 timeout: 6000 // 6sec
75735             };
75736             var _locating = uiLoading(context).message(_t('geolocate.locating')).blocking(true);
75737             var _layer = context.layers().layer('geolocate');
75738             var _position;
75739             var _extent;
75740             var _timeoutID;
75741             var _button = select(null);
75742
75743             function click() {
75744                 if (context.inIntro()) { return; }
75745                 if (!_layer.enabled() && !_locating.isShown()) {
75746
75747                     // This timeout ensures that we still call finish() even if
75748                     // the user declines to share their location in Firefox
75749                     _timeoutID = setTimeout(error, 10000 /* 10sec */ );
75750
75751                     context.container().call(_locating);
75752                     // get the latest position even if we already have one
75753                     navigator.geolocation.getCurrentPosition(success, error, _geolocationOptions);
75754                 } else {
75755                     _locating.close();
75756                     _layer.enabled(null, false);
75757                     updateButtonState();
75758                 }
75759             }
75760
75761             function zoomTo() {
75762                 context.enter(modeBrowse(context));
75763
75764                 var map = context.map();
75765                 _layer.enabled(_position, true);
75766                 updateButtonState();
75767                 map.centerZoomEase(_extent.center(), Math.min(20, map.extentZoom(_extent)));
75768             }
75769
75770             function success(geolocation) {
75771                 _position = geolocation;
75772                 var coords = _position.coords;
75773                 _extent = geoExtent([coords.longitude, coords.latitude]).padByMeters(coords.accuracy);
75774                 zoomTo();
75775                 finish();
75776             }
75777
75778             function error() {
75779                 if (_position) {
75780                     // use the position from a previous call if we have one
75781                     zoomTo();
75782                 } else {
75783                     context.ui().flash
75784                         .text(_t('geolocate.location_unavailable'))
75785                         .iconName('#iD-icon-geolocate')();
75786                 }
75787
75788                 finish();
75789             }
75790
75791             function finish() {
75792                 _locating.close();  // unblock ui
75793                 if (_timeoutID) { clearTimeout(_timeoutID); }
75794                 _timeoutID = undefined;
75795             }
75796
75797             function updateButtonState() {
75798                 _button.classed('active', _layer.enabled());
75799             }
75800
75801             return function(selection) {
75802                 if (!navigator.geolocation || !navigator.geolocation.getCurrentPosition) { return; }
75803
75804                 _button = selection
75805                     .append('button')
75806                     .on('click', click)
75807                     .call(svgIcon('#iD-icon-geolocate', 'light'))
75808                     .call(uiTooltip()
75809                         .placement((_mainLocalizer.textDirection() === 'rtl') ? 'right' : 'left')
75810                         .title(_t('geolocate.title'))
75811                         .keys([_t('geolocate.key')])
75812                     );
75813
75814                 context.keybinding().on(_t('geolocate.key'), click);
75815             };
75816         }
75817
75818         function uiPanelBackground(context) {
75819             var background = context.background();
75820             var currSourceName = null;
75821             var metadata = {};
75822             var metadataKeys = [
75823                 'zoom', 'vintage', 'source', 'description', 'resolution', 'accuracy'
75824             ];
75825
75826             var debouncedRedraw = debounce(redraw, 250);
75827
75828             function redraw(selection) {
75829                 var source = background.baseLayerSource();
75830                 if (!source) { return; }
75831
75832                 var isDG = (source.id.match(/^DigitalGlobe/i) !== null);
75833
75834                 if (currSourceName !== source.name()) {
75835                     currSourceName = source.name();
75836                     metadata = {};
75837                 }
75838
75839                 selection.html('');
75840
75841                 var list = selection
75842                     .append('ul')
75843                     .attr('class', 'background-info');
75844
75845                 list
75846                     .append('li')
75847                     .text(currSourceName);
75848
75849                 metadataKeys.forEach(function(k) {
75850                     // DigitalGlobe vintage is available in raster layers for now.
75851                     if (isDG && k === 'vintage') { return; }
75852
75853                     list
75854                         .append('li')
75855                         .attr('class', 'background-info-list-' + k)
75856                         .classed('hide', !metadata[k])
75857                         .text(_t('info_panels.background.' + k) + ':')
75858                         .append('span')
75859                         .attr('class', 'background-info-span-' + k)
75860                         .text(metadata[k]);
75861                 });
75862
75863                 debouncedGetMetadata(selection);
75864
75865                 var toggleTiles = context.getDebug('tile') ? 'hide_tiles' : 'show_tiles';
75866
75867                 selection
75868                     .append('a')
75869                     .text(_t('info_panels.background.' + toggleTiles))
75870                     .attr('href', '#')
75871                     .attr('class', 'button button-toggle-tiles')
75872                     .on('click', function() {
75873                         event.preventDefault();
75874                         context.setDebug('tile', !context.getDebug('tile'));
75875                         selection.call(redraw);
75876                     });
75877
75878                 if (isDG) {
75879                     var key = source.id + '-vintage';
75880                     var sourceVintage = context.background().findSource(key);
75881                     var showsVintage = context.background().showsLayer(sourceVintage);
75882                     var toggleVintage = showsVintage ? 'hide_vintage' : 'show_vintage';
75883                     selection
75884                         .append('a')
75885                         .text(_t('info_panels.background.' + toggleVintage))
75886                         .attr('href', '#')
75887                         .attr('class', 'button button-toggle-vintage')
75888                         .on('click', function() {
75889                             event.preventDefault();
75890                             context.background().toggleOverlayLayer(sourceVintage);
75891                             selection.call(redraw);
75892                         });
75893                 }
75894
75895                 // disable if necessary
75896                 ['DigitalGlobe-Premium', 'DigitalGlobe-Standard'].forEach(function(layerId) {
75897                     if (source.id !== layerId) {
75898                         var key = layerId + '-vintage';
75899                         var sourceVintage = context.background().findSource(key);
75900                         if (context.background().showsLayer(sourceVintage)) {
75901                             context.background().toggleOverlayLayer(sourceVintage);
75902                         }
75903                     }
75904                 });
75905             }
75906
75907
75908             var debouncedGetMetadata = debounce(getMetadata, 250);
75909
75910             function getMetadata(selection) {
75911                 var tile = context.container().select('.layer-background img.tile-center');   // tile near viewport center
75912                 if (tile.empty()) { return; }
75913
75914                 var sourceName = currSourceName;
75915                 var d = tile.datum();
75916                 var zoom = (d && d.length >= 3 && d[2]) || Math.floor(context.map().zoom());
75917                 var center = context.map().center();
75918
75919                 // update zoom
75920                 metadata.zoom = String(zoom);
75921                 selection.selectAll('.background-info-list-zoom')
75922                     .classed('hide', false)
75923                     .selectAll('.background-info-span-zoom')
75924                     .text(metadata.zoom);
75925
75926                 if (!d || !d.length >= 3) { return; }
75927
75928                 background.baseLayerSource().getMetadata(center, d, function(err, result) {
75929                     if (err || currSourceName !== sourceName) { return; }
75930
75931                     // update vintage
75932                     var vintage = result.vintage;
75933                     metadata.vintage = (vintage && vintage.range) || _t('info_panels.background.unknown');
75934                     selection.selectAll('.background-info-list-vintage')
75935                         .classed('hide', false)
75936                         .selectAll('.background-info-span-vintage')
75937                         .text(metadata.vintage);
75938
75939                     // update other metdata
75940                     metadataKeys.forEach(function(k) {
75941                         if (k === 'zoom' || k === 'vintage') { return; }  // done already
75942                         var val = result[k];
75943                         metadata[k] = val;
75944                         selection.selectAll('.background-info-list-' + k)
75945                             .classed('hide', !val)
75946                             .selectAll('.background-info-span-' + k)
75947                             .text(val);
75948                     });
75949                 });
75950             }
75951
75952
75953             var panel = function(selection) {
75954                 selection.call(redraw);
75955
75956                 context.map()
75957                     .on('drawn.info-background', function() {
75958                         selection.call(debouncedRedraw);
75959                     })
75960                     .on('move.info-background', function() {
75961                         selection.call(debouncedGetMetadata);
75962                     });
75963
75964             };
75965
75966             panel.off = function() {
75967                 context.map()
75968                     .on('drawn.info-background', null)
75969                     .on('move.info-background', null);
75970             };
75971
75972             panel.id = 'background';
75973             panel.title = _t('info_panels.background.title');
75974             panel.key = _t('info_panels.background.key');
75975
75976
75977             return panel;
75978         }
75979
75980         function uiPanelHistory(context) {
75981             var osm;
75982
75983             function displayTimestamp(timestamp) {
75984                 if (!timestamp) { return _t('info_panels.history.unknown'); }
75985                 var options = {
75986                     day: 'numeric', month: 'short', year: 'numeric',
75987                     hour: 'numeric', minute: 'numeric', second: 'numeric'
75988                 };
75989                 var d = new Date(timestamp);
75990                 if (isNaN(d.getTime())) { return _t('info_panels.history.unknown'); }
75991                 return d.toLocaleString(_mainLocalizer.localeCode(), options);
75992             }
75993
75994
75995             function displayUser(selection, userName) {
75996                 if (!userName) {
75997                     selection
75998                         .append('span')
75999                         .text(_t('info_panels.history.unknown'));
76000                     return;
76001                 }
76002
76003                 selection
76004                     .append('span')
76005                     .attr('class', 'user-name')
76006                     .text(userName);
76007
76008                 var links = selection
76009                     .append('div')
76010                     .attr('class', 'links');
76011
76012                 if (osm) {
76013                     links
76014                         .append('a')
76015                         .attr('class', 'user-osm-link')
76016                         .attr('href', osm.userURL(userName))
76017                         .attr('target', '_blank')
76018                         .attr('tabindex', -1)
76019                         .text('OSM');
76020                 }
76021
76022                 links
76023                     .append('a')
76024                     .attr('class', 'user-hdyc-link')
76025                     .attr('href', 'https://hdyc.neis-one.org/?' + userName)
76026                     .attr('target', '_blank')
76027                     .attr('tabindex', -1)
76028                     .text('HDYC');
76029             }
76030
76031
76032             function displayChangeset(selection, changeset) {
76033                 if (!changeset) {
76034                     selection
76035                         .append('span')
76036                         .text(_t('info_panels.history.unknown'));
76037                     return;
76038                 }
76039
76040                 selection
76041                     .append('span')
76042                     .attr('class', 'changeset-id')
76043                     .text(changeset);
76044
76045                 var links = selection
76046                     .append('div')
76047                     .attr('class', 'links');
76048
76049                 if (osm) {
76050                     links
76051                         .append('a')
76052                         .attr('class', 'changeset-osm-link')
76053                         .attr('href', osm.changesetURL(changeset))
76054                         .attr('target', '_blank')
76055                         .attr('tabindex', -1)
76056                         .text('OSM');
76057                 }
76058
76059                 links
76060                     .append('a')
76061                     .attr('class', 'changeset-osmcha-link')
76062                     .attr('href', 'https://osmcha.org/changesets/' + changeset)
76063                     .attr('target', '_blank')
76064                     .attr('tabindex', -1)
76065                     .text('OSMCha');
76066
76067                 links
76068                     .append('a')
76069                     .attr('class', 'changeset-achavi-link')
76070                     .attr('href', 'https://overpass-api.de/achavi/?changeset=' + changeset)
76071                     .attr('target', '_blank')
76072                     .attr('tabindex', -1)
76073                     .text('Achavi');
76074             }
76075
76076
76077             function redraw(selection) {
76078                 var selectedNoteID = context.selectedNoteID();
76079                 osm = context.connection();
76080
76081                 var selected, note, entity;
76082                 if (selectedNoteID && osm) {       // selected 1 note
76083                     selected = [ _t('note.note') + ' ' + selectedNoteID ];
76084                     note = osm.getNote(selectedNoteID);
76085                 } else {                           // selected 1..n entities
76086                     selected = context.selectedIDs()
76087                         .filter(function(e) { return context.hasEntity(e); });
76088                     if (selected.length) {
76089                         entity = context.entity(selected[0]);
76090                     }
76091                 }
76092
76093                 var singular = selected.length === 1 ? selected[0] : null;
76094
76095                 selection.html('');
76096
76097                 selection
76098                     .append('h4')
76099                     .attr('class', 'history-heading')
76100                     .text(singular || _t('info_panels.history.selected', { n: selected.length }));
76101
76102                 if (!singular) { return; }
76103
76104                 if (entity) {
76105                     selection.call(redrawEntity, entity);
76106                 } else if (note) {
76107                     selection.call(redrawNote, note);
76108                 }
76109             }
76110
76111
76112             function redrawNote(selection, note) {
76113                 if (!note || note.isNew()) {
76114                     selection
76115                         .append('div')
76116                         .text(_t('info_panels.history.note_no_history'));
76117                     return;
76118                 }
76119
76120                 var list = selection
76121                     .append('ul');
76122
76123                 list
76124                     .append('li')
76125                     .text(_t('info_panels.history.note_comments') + ':')
76126                     .append('span')
76127                     .text(note.comments.length);
76128
76129                 if (note.comments.length) {
76130                     list
76131                         .append('li')
76132                         .text(_t('info_panels.history.note_created_date') + ':')
76133                         .append('span')
76134                         .text(displayTimestamp(note.comments[0].date));
76135
76136                     list
76137                         .append('li')
76138                         .text(_t('info_panels.history.note_created_user') + ':')
76139                         .call(displayUser, note.comments[0].user);
76140                 }
76141
76142                 if (osm) {
76143                     selection
76144                         .append('a')
76145                         .attr('class', 'view-history-on-osm')
76146                         .attr('target', '_blank')
76147                         .attr('tabindex', -1)
76148                         .attr('href', osm.noteURL(note))
76149                         .call(svgIcon('#iD-icon-out-link', 'inline'))
76150                         .append('span')
76151                         .text(_t('info_panels.history.note_link_text'));
76152                 }
76153             }
76154
76155
76156             function redrawEntity(selection, entity) {
76157                 if (!entity || entity.isNew()) {
76158                     selection
76159                         .append('div')
76160                         .text(_t('info_panels.history.no_history'));
76161                     return;
76162                 }
76163
76164                 var links = selection
76165                     .append('div')
76166                     .attr('class', 'links');
76167
76168                 if (osm) {
76169                     links
76170                         .append('a')
76171                         .attr('class', 'view-history-on-osm')
76172                         .attr('href', osm.historyURL(entity))
76173                         .attr('target', '_blank')
76174                         .attr('tabindex', -1)
76175                         .attr('title', _t('info_panels.history.link_text'))
76176                         .text('OSM');
76177                 }
76178                 links
76179                     .append('a')
76180                     .attr('class', 'pewu-history-viewer-link')
76181                     .attr('href', 'https://pewu.github.io/osm-history/#/' + entity.type + '/' + entity.osmId())
76182                     .attr('target', '_blank')
76183                     .attr('tabindex', -1)
76184                     .text('PeWu');
76185
76186                 var list = selection
76187                     .append('ul');
76188
76189                 list
76190                     .append('li')
76191                     .text(_t('info_panels.history.version') + ':')
76192                     .append('span')
76193                     .text(entity.version);
76194
76195                 list
76196                     .append('li')
76197                     .text(_t('info_panels.history.last_edit') + ':')
76198                     .append('span')
76199                     .text(displayTimestamp(entity.timestamp));
76200
76201                 list
76202                     .append('li')
76203                     .text(_t('info_panels.history.edited_by') + ':')
76204                     .call(displayUser, entity.user);
76205
76206                 list
76207                     .append('li')
76208                     .text(_t('info_panels.history.changeset') + ':')
76209                     .call(displayChangeset, entity.changeset);
76210             }
76211
76212
76213             var panel = function(selection) {
76214                 selection.call(redraw);
76215
76216                 context.map()
76217                     .on('drawn.info-history', function() {
76218                         selection.call(redraw);
76219                     });
76220
76221                 context
76222                     .on('enter.info-history', function() {
76223                         selection.call(redraw);
76224                     });
76225             };
76226
76227             panel.off = function() {
76228                 context.map().on('drawn.info-history', null);
76229                 context.on('enter.info-history', null);
76230             };
76231
76232             panel.id = 'history';
76233             panel.title = _t('info_panels.history.title');
76234             panel.key = _t('info_panels.history.key');
76235
76236
76237             return panel;
76238         }
76239
76240         var OSM_PRECISION = 7;
76241
76242         /**
76243          * Returns a localized representation of the given length measurement.
76244          *
76245          * @param {Number} m area in meters
76246          * @param {Boolean} isImperial true for U.S. customary units; false for metric
76247          */
76248         function displayLength(m, isImperial) {
76249             var d = m * (isImperial ? 3.28084 : 1);
76250             var unit;
76251
76252             if (isImperial) {
76253                 if (d >= 5280) {
76254                     d /= 5280;
76255                     unit = 'miles';
76256                 } else {
76257                     unit = 'feet';
76258                 }
76259             } else {
76260                 if (d >= 1000) {
76261                     d /= 1000;
76262                     unit = 'kilometers';
76263                 } else {
76264                     unit = 'meters';
76265                 }
76266             }
76267
76268             return _t('units.' + unit, {
76269                 quantity: d.toLocaleString(_mainLocalizer.localeCode(), {
76270                     maximumSignificantDigits: 4
76271                 })
76272             });
76273         }
76274
76275         /**
76276          * Returns a localized representation of the given area measurement.
76277          *
76278          * @param {Number} m2 area in square meters
76279          * @param {Boolean} isImperial true for U.S. customary units; false for metric
76280          */
76281         function displayArea(m2, isImperial) {
76282             var locale = _mainLocalizer.localeCode();
76283             var d = m2 * (isImperial ? 10.7639111056 : 1);
76284             var d1, d2, area;
76285             var unit1 = '';
76286             var unit2 = '';
76287
76288             if (isImperial) {
76289                 if (d >= 6969600) { // > 0.25mi² show mi²
76290                     d1 = d / 27878400;
76291                     unit1 = 'square_miles';
76292                 } else {
76293                     d1 = d;
76294                     unit1 = 'square_feet';
76295                 }
76296
76297                 if (d > 4356 && d < 43560000) { // 0.1 - 1000 acres
76298                     d2 = d / 43560;
76299                     unit2 = 'acres';
76300                 }
76301
76302             } else {
76303                 if (d >= 250000) { // > 0.25km² show km²
76304                     d1 = d / 1000000;
76305                     unit1 = 'square_kilometers';
76306                 } else {
76307                     d1 = d;
76308                     unit1 = 'square_meters';
76309                 }
76310
76311                 if (d > 1000 && d < 10000000) { // 0.1 - 1000 hectares
76312                     d2 = d / 10000;
76313                     unit2 = 'hectares';
76314                 }
76315             }
76316
76317             area = _t('units.' + unit1, {
76318                 quantity: d1.toLocaleString(locale, {
76319                     maximumSignificantDigits: 4
76320                 })
76321             });
76322
76323             if (d2) {
76324                 return _t('units.area_pair', {
76325                     area1: area,
76326                     area2: _t('units.' + unit2, {
76327                         quantity: d2.toLocaleString(locale, {
76328                             maximumSignificantDigits: 2
76329                         })
76330                     })
76331                 });
76332             } else {
76333                 return area;
76334             }
76335         }
76336
76337         function wrap(x, min, max) {
76338             var d = max - min;
76339             return ((x - min) % d + d) % d + min;
76340         }
76341
76342         function clamp$1(x, min, max) {
76343             return Math.max(min, Math.min(x, max));
76344         }
76345
76346         function displayCoordinate(deg, pos, neg) {
76347             var locale = _mainLocalizer.localeCode();
76348             var min = (Math.abs(deg) - Math.floor(Math.abs(deg))) * 60;
76349             var sec = (min - Math.floor(min)) * 60;
76350             var displayDegrees = _t('units.arcdegrees', {
76351                 quantity: Math.floor(Math.abs(deg)).toLocaleString(locale)
76352             });
76353             var displayCoordinate;
76354
76355             if (Math.floor(sec) > 0) {
76356                 displayCoordinate = displayDegrees +
76357                     _t('units.arcminutes', {
76358                         quantity: Math.floor(min).toLocaleString(locale)
76359                     }) +
76360                     _t('units.arcseconds', {
76361                         quantity: Math.round(sec).toLocaleString(locale)
76362                     });
76363             } else if (Math.floor(min) > 0) {
76364                 displayCoordinate = displayDegrees +
76365                     _t('units.arcminutes', {
76366                         quantity: Math.round(min).toLocaleString(locale)
76367                     });
76368             } else {
76369                 displayCoordinate = _t('units.arcdegrees', {
76370                     quantity: Math.round(Math.abs(deg)).toLocaleString(locale)
76371                 });
76372             }
76373
76374             if (deg === 0) {
76375                 return displayCoordinate;
76376             } else {
76377                 return _t('units.coordinate', {
76378                     coordinate: displayCoordinate,
76379                     direction: _t('units.' + (deg > 0 ? pos : neg))
76380                 });
76381             }
76382         }
76383
76384         /**
76385          * Returns given coordinate pair in degree-minute-second format.
76386          *
76387          * @param {Array<Number>} coord longitude and latitude
76388          */
76389         function dmsCoordinatePair(coord) {
76390             return _t('units.coordinate_pair', {
76391                 latitude: displayCoordinate(clamp$1(coord[1], -90, 90), 'north', 'south'),
76392                 longitude: displayCoordinate(wrap(coord[0], -180, 180), 'east', 'west')
76393             });
76394         }
76395
76396         /**
76397          * Returns the given coordinate pair in decimal format.
76398          * note: unlocalized to avoid comma ambiguity - see #4765
76399          *
76400          * @param {Array<Number>} coord longitude and latitude
76401          */
76402         function decimalCoordinatePair(coord) {
76403             return _t('units.coordinate_pair', {
76404                 latitude: clamp$1(coord[1], -90, 90).toFixed(OSM_PRECISION),
76405                 longitude: wrap(coord[0], -180, 180).toFixed(OSM_PRECISION)
76406             });
76407         }
76408
76409         function uiPanelLocation(context) {
76410             var currLocation = '';
76411
76412
76413             function redraw(selection) {
76414                 selection.html('');
76415
76416                 var list = selection
76417                     .append('ul');
76418
76419                 // Mouse coordinates
76420                 var coord = context.map().mouseCoordinates();
76421                 if (coord.some(isNaN)) {
76422                     coord = context.map().center();
76423                 }
76424
76425                 list
76426                     .append('li')
76427                     .text(dmsCoordinatePair(coord))
76428                     .append('li')
76429                     .text(decimalCoordinatePair(coord));
76430
76431                 // Location Info
76432                 selection
76433                     .append('div')
76434                     .attr('class', 'location-info')
76435                     .text(currLocation || ' ');
76436
76437                 debouncedGetLocation(selection, coord);
76438             }
76439
76440
76441             var debouncedGetLocation = debounce(getLocation, 250);
76442             function getLocation(selection, coord) {
76443                 if (!services.geocoder) {
76444                     currLocation = _t('info_panels.location.unknown_location');
76445                     selection.selectAll('.location-info')
76446                         .text(currLocation);
76447                 } else {
76448                     services.geocoder.reverse(coord, function(err, result) {
76449                         currLocation = result ? result.display_name : _t('info_panels.location.unknown_location');
76450                         selection.selectAll('.location-info')
76451                             .text(currLocation);
76452                     });
76453                 }
76454             }
76455
76456
76457             var panel = function(selection) {
76458                 selection.call(redraw);
76459
76460                 context.surface()
76461                     .on(('PointerEvent' in window ? 'pointer' : 'mouse') + 'move.info-location', function() {
76462                         selection.call(redraw);
76463                     });
76464             };
76465
76466             panel.off = function() {
76467                 context.surface()
76468                     .on('.info-location', null);
76469             };
76470
76471             panel.id = 'location';
76472             panel.title = _t('info_panels.location.title');
76473             panel.key = _t('info_panels.location.key');
76474
76475
76476             return panel;
76477         }
76478
76479         function uiPanelMeasurement(context) {
76480             var locale = _mainLocalizer.localeCode();
76481             var isImperial = !_mainLocalizer.usesMetric();
76482
76483
76484             function radiansToMeters(r) {
76485                 // using WGS84 authalic radius (6371007.1809 m)
76486                 return r * 6371007.1809;
76487             }
76488
76489             function steradiansToSqmeters(r) {
76490                 // http://gis.stackexchange.com/a/124857/40446
76491                 return r / (4 * Math.PI) * 510065621724000;
76492             }
76493
76494
76495             function toLineString(feature) {
76496                 if (feature.type === 'LineString') { return feature; }
76497
76498                 var result = { type: 'LineString', coordinates: [] };
76499                 if (feature.type === 'Polygon') {
76500                     result.coordinates = feature.coordinates[0];
76501                 } else if (feature.type === 'MultiPolygon') {
76502                     result.coordinates = feature.coordinates[0][0];
76503                 }
76504
76505                 return result;
76506             }
76507
76508
76509             function redraw(selection) {
76510                 var graph = context.graph();
76511                 var selectedNoteID = context.selectedNoteID();
76512                 var osm = services.osm;
76513
76514                 var heading;
76515                 var center, location, centroid;
76516                 var closed, geometry;
76517                 var totalNodeCount, length = 0, area = 0;
76518
76519                 if (selectedNoteID && osm) {       // selected 1 note
76520
76521                     var note = osm.getNote(selectedNoteID);
76522                     heading = _t('note.note') + ' ' + selectedNoteID;
76523                     location = note.loc;
76524                     geometry = 'note';
76525
76526                 } else {                           // selected 1..n entities
76527                     var selectedIDs = context.selectedIDs().filter(function(id) {
76528                         return context.hasEntity(id);
76529                     });
76530                     var selected = selectedIDs.map(function(id) {
76531                         return context.entity(id);
76532                     });
76533
76534                     heading = selected.length === 1 ? selected[0].id :
76535                         _t('info_panels.measurement.selected', { n: selected.length.toLocaleString(locale) });
76536
76537                     if (selected.length) {
76538                         var extent = geoExtent();
76539                         for (var i in selected) {
76540                             var entity = selected[i];
76541                             extent._extend(entity.extent(graph));
76542
76543                             geometry = entity.geometry(graph);
76544                             if (geometry === 'line' || geometry === 'area') {
76545                                 closed = (entity.type === 'relation') || (entity.isClosed() && !entity.isDegenerate());
76546                                 var feature = entity.asGeoJSON(graph);
76547                                 length += radiansToMeters(d3_geoLength(toLineString(feature)));
76548                                 centroid = d3_geoCentroid(feature);
76549                                 if (closed) {
76550                                     area += steradiansToSqmeters(entity.area(graph));
76551                                 }
76552                             }
76553                         }
76554
76555                         if (selected.length > 1) {
76556                             geometry = null;
76557                             closed = null;
76558                             centroid = null;
76559                         }
76560
76561                         if (selected.length === 1 && selected[0].type === 'node') {
76562                             location = selected[0].loc;
76563                         } else {
76564                             totalNodeCount = utilGetAllNodes(selectedIDs, context.graph()).length;
76565                         }
76566
76567                         if (!location && !centroid) {
76568                             center = extent.center();
76569                         }
76570                     }
76571                 }
76572
76573                 selection.html('');
76574
76575                 if (heading) {
76576                     selection
76577                         .append('h4')
76578                         .attr('class', 'measurement-heading')
76579                         .text(heading);
76580                 }
76581
76582                 var list = selection
76583                     .append('ul');
76584                 var coordItem;
76585
76586                 if (geometry) {
76587                     list
76588                         .append('li')
76589                         .text(_t('info_panels.measurement.geometry') + ':')
76590                         .append('span')
76591                         .text(
76592                             closed ? _t('info_panels.measurement.closed_' + geometry) : _t('geometry.' + geometry)
76593                         );
76594                 }
76595
76596                 if (totalNodeCount) {
76597                     list
76598                         .append('li')
76599                         .text(_t('info_panels.measurement.node_count') + ':')
76600                         .append('span')
76601                         .text(totalNodeCount.toLocaleString(locale));
76602                 }
76603
76604                 if (area) {
76605                     list
76606                         .append('li')
76607                         .text(_t('info_panels.measurement.area') + ':')
76608                         .append('span')
76609                         .text(displayArea(area, isImperial));
76610                 }
76611
76612                 if (length) {
76613                     var lengthLabel = _t('info_panels.measurement.' + (closed ? 'perimeter' : 'length'));
76614                     list
76615                         .append('li')
76616                         .text(lengthLabel + ':')
76617                         .append('span')
76618                         .text(displayLength(length, isImperial));
76619                 }
76620
76621                 if (location) {
76622                     coordItem = list
76623                         .append('li')
76624                         .text(_t('info_panels.measurement.location') + ':');
76625                     coordItem.append('span')
76626                         .text(dmsCoordinatePair(location));
76627                     coordItem.append('span')
76628                         .text(decimalCoordinatePair(location));
76629                 }
76630
76631                 if (centroid) {
76632                     coordItem = list
76633                         .append('li')
76634                         .text(_t('info_panels.measurement.centroid') + ':');
76635                     coordItem.append('span')
76636                         .text(dmsCoordinatePair(centroid));
76637                     coordItem.append('span')
76638                         .text(decimalCoordinatePair(centroid));
76639                 }
76640
76641                 if (center) {
76642                     coordItem = list
76643                         .append('li')
76644                         .text(_t('info_panels.measurement.center') + ':');
76645                     coordItem.append('span')
76646                         .text(dmsCoordinatePair(center));
76647                     coordItem.append('span')
76648                         .text(decimalCoordinatePair(center));
76649                 }
76650
76651                 if (length || area) {
76652                     var toggle  = isImperial ? 'imperial' : 'metric';
76653                     selection
76654                         .append('a')
76655                         .text(_t('info_panels.measurement.' + toggle))
76656                         .attr('href', '#')
76657                         .attr('class', 'button button-toggle-units')
76658                         .on('click', function() {
76659                             event.preventDefault();
76660                             isImperial = !isImperial;
76661                             selection.call(redraw);
76662                         });
76663                 }
76664             }
76665
76666
76667             var panel = function(selection) {
76668                 selection.call(redraw);
76669
76670                 context.map()
76671                     .on('drawn.info-measurement', function() {
76672                         selection.call(redraw);
76673                     });
76674
76675                 context
76676                     .on('enter.info-measurement', function() {
76677                         selection.call(redraw);
76678                     });
76679             };
76680
76681             panel.off = function() {
76682                 context.map().on('drawn.info-measurement', null);
76683                 context.on('enter.info-measurement', null);
76684             };
76685
76686             panel.id = 'measurement';
76687             panel.title = _t('info_panels.measurement.title');
76688             panel.key = _t('info_panels.measurement.key');
76689
76690
76691             return panel;
76692         }
76693
76694         var uiInfoPanels = {
76695             background: uiPanelBackground,
76696             history: uiPanelHistory,
76697             location: uiPanelLocation,
76698             measurement: uiPanelMeasurement,
76699         };
76700
76701         function uiInfo(context) {
76702             var ids = Object.keys(uiInfoPanels);
76703             var wasActive = ['measurement'];
76704             var panels = {};
76705             var active = {};
76706
76707             // create panels
76708             ids.forEach(function(k) {
76709                 if (!panels[k]) {
76710                     panels[k] = uiInfoPanels[k](context);
76711                     active[k] = false;
76712                 }
76713             });
76714
76715
76716             function info(selection) {
76717
76718                 function redraw() {
76719                     var activeids = ids.filter(function(k) { return active[k]; }).sort();
76720
76721                     var containers = infoPanels.selectAll('.panel-container')
76722                         .data(activeids, function(k) { return k; });
76723
76724                     containers.exit()
76725                         .style('opacity', 1)
76726                         .transition()
76727                         .duration(200)
76728                         .style('opacity', 0)
76729                         .on('end', function(d) {
76730                             select(this)
76731                                 .call(panels[d].off)
76732                                 .remove();
76733                         });
76734
76735                     var enter = containers.enter()
76736                         .append('div')
76737                         .attr('class', function(d) { return 'fillD2 panel-container panel-container-' + d; });
76738
76739                     enter
76740                         .style('opacity', 0)
76741                         .transition()
76742                         .duration(200)
76743                         .style('opacity', 1);
76744
76745                     var title = enter
76746                         .append('div')
76747                         .attr('class', 'panel-title fillD2');
76748
76749                     title
76750                         .append('h3')
76751                         .text(function(d) { return panels[d].title; });
76752
76753                     title
76754                         .append('button')
76755                         .attr('class', 'close')
76756                         .on('click', function (d) { info.toggle(d); })
76757                         .call(svgIcon('#iD-icon-close'));
76758
76759                     enter
76760                         .append('div')
76761                         .attr('class', function(d) { return 'panel-content panel-content-' + d; });
76762
76763
76764                     // redraw the panels
76765                     infoPanels.selectAll('.panel-content')
76766                         .each(function(d) {
76767                             select(this).call(panels[d]);
76768                         });
76769                 }
76770
76771
76772                 info.toggle = function(which) {
76773                     if (event) {
76774                         event.stopImmediatePropagation();
76775                         event.preventDefault();
76776                     }
76777
76778                     var activeids = ids.filter(function(k) { return active[k]; });
76779
76780                     if (which) {  // toggle one
76781                         active[which] = !active[which];
76782                         if (activeids.length === 1 && activeids[0] === which) {  // none active anymore
76783                             wasActive = [which];
76784                         }
76785
76786                         context.container().select('.' + which + '-panel-toggle-item')
76787                             .classed('active', active[which])
76788                             .select('input')
76789                             .property('checked', active[which]);
76790
76791                     } else {      // toggle all
76792                         if (activeids.length) {
76793                             wasActive = activeids;
76794                             activeids.forEach(function(k) { active[k] = false; });
76795                         } else {
76796                             wasActive.forEach(function(k) { active[k] = true; });
76797                         }
76798                     }
76799
76800                     redraw();
76801                 };
76802
76803
76804                 var infoPanels = selection.selectAll('.info-panels')
76805                     .data([0]);
76806
76807                 infoPanels = infoPanels.enter()
76808                     .append('div')
76809                     .attr('class', 'info-panels')
76810                     .merge(infoPanels);
76811
76812                 redraw();
76813
76814                 context.keybinding()
76815                     .on(uiCmd('⌘' + _t('info_panels.key')), info.toggle);
76816
76817                 ids.forEach(function(k) {
76818                     var key = _t('info_panels.' + k + '.key', { default: null });
76819                     if (!key) { return; }
76820                     context.keybinding()
76821                         .on(uiCmd('⌘⇧' + key), function() { info.toggle(k); });
76822                 });
76823             }
76824
76825             return info;
76826         }
76827
76828         function pointBox(loc, context) {
76829             var rect = context.surfaceRect();
76830             var point = context.curtainProjection(loc);
76831             return {
76832                 left: point[0] + rect.left - 40,
76833                 top: point[1] + rect.top - 60,
76834                 width: 80,
76835                 height: 90
76836             };
76837         }
76838
76839
76840         function pad(locOrBox, padding, context) {
76841             var box;
76842             if (locOrBox instanceof Array) {
76843                 var rect = context.surfaceRect();
76844                 var point = context.curtainProjection(locOrBox);
76845                 box = {
76846                     left: point[0] + rect.left,
76847                     top: point[1] + rect.top
76848                 };
76849             } else {
76850                 box = locOrBox;
76851             }
76852
76853             return {
76854                 left: box.left - padding,
76855                 top: box.top - padding,
76856                 width: (box.width || 0) + 2 * padding,
76857                 height: (box.width || 0) + 2 * padding
76858             };
76859         }
76860
76861
76862         function icon(name, svgklass, useklass) {
76863             return '<svg class="icon ' + (svgklass || '') + '">' +
76864                  '<use xlink:href="' + name + '"' +
76865                  (useklass ? ' class="' + useklass + '"' : '') + '></use></svg>';
76866         }
76867
76868         var helpStringReplacements;
76869
76870         // Returns the localized string for `id` with a standardized set of icon, key, and
76871         // label replacements suitable for tutorials and documentation. Optionally supplemented
76872         // with custom `replacements`
76873         function helpString(id, replacements) {
76874             // only load these the first time
76875             if (!helpStringReplacements) { helpStringReplacements = {
76876                 // insert icons corresponding to various UI elements
76877                 point_icon: icon('#iD-icon-point', 'pre-text'),
76878                 line_icon: icon('#iD-icon-line', 'pre-text'),
76879                 area_icon: icon('#iD-icon-area', 'pre-text'),
76880                 note_icon: icon('#iD-icon-note', 'pre-text add-note'),
76881                 plus: icon('#iD-icon-plus', 'pre-text'),
76882                 minus: icon('#iD-icon-minus', 'pre-text'),
76883                 move_icon: icon('#iD-operation-move', 'pre-text operation'),
76884                 merge_icon: icon('#iD-operation-merge', 'pre-text operation'),
76885                 delete_icon: icon('#iD-operation-delete', 'pre-text operation'),
76886                 circularize_icon: icon('#iD-operation-circularize', 'pre-text operation'),
76887                 split_icon: icon('#iD-operation-split', 'pre-text operation'),
76888                 orthogonalize_icon: icon('#iD-operation-orthogonalize', 'pre-text operation'),
76889                 disconnect_icon: icon('#iD-operation-disconnect', 'pre-text operation'),
76890                 layers_icon: icon('#iD-icon-layers', 'pre-text'),
76891                 data_icon: icon('#iD-icon-data', 'pre-text'),
76892                 inspect: icon('#iD-icon-inspect', 'pre-text'),
76893                 help_icon: icon('#iD-icon-help', 'pre-text'),
76894                 undo_icon: icon(_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-redo' : '#iD-icon-undo', 'pre-text'),
76895                 redo_icon: icon(_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-undo' : '#iD-icon-redo', 'pre-text'),
76896                 save_icon: icon('#iD-icon-save', 'pre-text'),
76897                 leftclick: icon('#iD-walkthrough-mouse-left', 'pre-text operation'),
76898                 rightclick: icon('#iD-walkthrough-mouse-right', 'pre-text operation'),
76899                 mousewheel_icon: icon('#iD-walkthrough-mousewheel', 'pre-text operation'),
76900                 tap_icon: icon('#iD-walkthrough-tap', 'pre-text operation'),
76901                 doubletap_icon: icon('#iD-walkthrough-doubletap', 'pre-text operation'),
76902                 longpress_icon: icon('#iD-walkthrough-longpress', 'pre-text operation'),
76903                 touchdrag_icon: icon('#iD-walkthrough-touchdrag', 'pre-text operation'),
76904                 pinch_icon: icon('#iD-walkthrough-pinch-apart', 'pre-text operation'),
76905
76906                 // insert keys; may be localized and platform-dependent
76907                 shift: uiCmd.display('⇧'),
76908                 alt: uiCmd.display('⌥'),
76909                 return: uiCmd.display('↵'),
76910                 esc: _t('shortcuts.key.esc'),
76911                 space: _t('shortcuts.key.space'),
76912                 add_note_key: _t('modes.add_note.key'),
76913                 help_key: _t('help.key'),
76914                 shortcuts_key: _t('shortcuts.toggle.key'),
76915
76916                 // reference localized UI labels directly so that they'll always match
76917                 save: _t('save.title'),
76918                 undo: _t('undo.title'),
76919                 redo: _t('redo.title'),
76920                 upload: _t('commit.save'),
76921                 point: _t('modes.add_point.title'),
76922                 line: _t('modes.add_line.title'),
76923                 area: _t('modes.add_area.title'),
76924                 note: _t('modes.add_note.title'),
76925                 delete: _t('operations.delete.title'),
76926                 move: _t('operations.move.title'),
76927                 orthogonalize: _t('operations.orthogonalize.title'),
76928                 circularize: _t('operations.circularize.title'),
76929                 merge: _t('operations.merge.title'),
76930                 disconnect: _t('operations.disconnect.title'),
76931                 split: _t('operations.split.title'),
76932                 map_data: _t('map_data.title'),
76933                 osm_notes: _t('map_data.layers.notes.title'),
76934                 fields: _t('inspector.fields'),
76935                 tags: _t('inspector.tags'),
76936                 relations: _t('inspector.relations'),
76937                 new_relation: _t('inspector.new_relation'),
76938                 turn_restrictions: _t('presets.fields.restrictions.label'),
76939                 background_settings: _t('background.description'),
76940                 imagery_offset: _t('background.fix_misalignment'),
76941                 start_the_walkthrough: _t('splash.walkthrough'),
76942                 help: _t('help.title'),
76943                 ok: _t('intro.ok')
76944             }; }
76945
76946             var reps;
76947             if (replacements) {
76948                 reps = Object.assign(replacements, helpStringReplacements);
76949             } else {
76950                 reps = helpStringReplacements;
76951             }
76952
76953             return _t(id, reps)
76954                  // use keyboard key styling for shortcuts
76955                 .replace(/\`(.*?)\`/g, '<kbd>$1</kbd>');
76956         }
76957
76958
76959         function slugify(text) {
76960             return text.toString().toLowerCase()
76961                 .replace(/\s+/g, '-')           // Replace spaces with -
76962                 .replace(/[^\w\-]+/g, '')       // Remove all non-word chars
76963                 .replace(/\-\-+/g, '-')         // Replace multiple - with single -
76964                 .replace(/^-+/, '')             // Trim - from start of text
76965                 .replace(/-+$/, '');            // Trim - from end of text
76966         }
76967
76968
76969         // console warning for missing walkthrough names
76970         var missingStrings = {};
76971         function checkKey(key, text) {
76972             if (_t(key, { default: undefined}) === undefined) {
76973                 if (missingStrings.hasOwnProperty(key)) { return; }  // warn once
76974                 missingStrings[key] = text;
76975                 var missing = key + ': ' + text;
76976                 if (typeof console !== 'undefined') { console.log(missing); } // eslint-disable-line
76977             }
76978         }
76979
76980
76981         function localize(obj) {
76982             var key;
76983
76984             // Assign name if entity has one..
76985             var name = obj.tags && obj.tags.name;
76986             if (name) {
76987                 key = 'intro.graph.name.' + slugify(name);
76988                 obj.tags.name = _t(key, { default: name });
76989                 checkKey(key, name);
76990             }
76991
76992             // Assign street name if entity has one..
76993             var street = obj.tags && obj.tags['addr:street'];
76994             if (street) {
76995                 key = 'intro.graph.name.' + slugify(street);
76996                 obj.tags['addr:street'] = _t(key, { default: street });
76997                 checkKey(key, street);
76998
76999                 // Add address details common across walkthrough..
77000                 var addrTags = [
77001                     'block_number', 'city', 'county', 'district', 'hamlet', 'neighbourhood',
77002                     'postcode', 'province', 'quarter', 'state', 'subdistrict', 'suburb'
77003                 ];
77004                 addrTags.forEach(function(k) {
77005                     var key = 'intro.graph.' + k;
77006                     var tag = 'addr:' + k;
77007                     var val = obj.tags && obj.tags[tag];
77008                     var str = _t(key, { default: val });
77009
77010                     if (str) {
77011                         if (str.match(/^<.*>$/) !== null) {
77012                             delete obj.tags[tag];
77013                         } else {
77014                             obj.tags[tag] = str;
77015                         }
77016                     }
77017                 });
77018             }
77019
77020             return obj;
77021         }
77022
77023
77024         // Used to detect squareness.. some duplicataion of code from actionOrthogonalize.
77025         function isMostlySquare(points) {
77026             // note: uses 15 here instead of the 12 from actionOrthogonalize because
77027             // actionOrthogonalize can actually straighten some larger angles as it iterates
77028             var threshold = 15; // degrees within right or straight
77029             var lowerBound = Math.cos((90 - threshold) * Math.PI / 180);  // near right
77030             var upperBound = Math.cos(threshold * Math.PI / 180);         // near straight
77031
77032             for (var i = 0; i < points.length; i++) {
77033                 var a = points[(i - 1 + points.length) % points.length];
77034                 var origin = points[i];
77035                 var b = points[(i + 1) % points.length];
77036
77037                 var dotp = geoVecNormalizedDot(a, b, origin);
77038                 var mag = Math.abs(dotp);
77039                 if (mag > lowerBound && mag < upperBound) {
77040                     return false;
77041                 }
77042             }
77043
77044             return true;
77045         }
77046
77047
77048         function selectMenuItem(context, operation) {
77049             return context.container().select('.edit-menu .edit-menu-item-' + operation);
77050         }
77051
77052
77053         function transitionTime(point1, point2) {
77054             var distance = geoSphericalDistance(point1, point2);
77055             if (distance === 0)
77056                 { return 0; }
77057             else if (distance < 80)
77058                 { return 500; }
77059             else
77060                 { return 1000; }
77061         }
77062
77063         // Tooltips and svg mask used to highlight certain features
77064         function uiCurtain(containerNode) {
77065
77066             var surface = select(null),
77067                 tooltip = select(null),
77068                 darkness = select(null);
77069
77070             function curtain(selection) {
77071                 surface = selection
77072                     .append('svg')
77073                     .attr('class', 'curtain')
77074                     .style('top', 0)
77075                     .style('left', 0);
77076
77077                 darkness = surface.append('path')
77078                     .attr('x', 0)
77079                     .attr('y', 0)
77080                     .attr('class', 'curtain-darkness');
77081
77082                 select(window).on('resize.curtain', resize);
77083
77084                 tooltip = selection.append('div')
77085                     .attr('class', 'tooltip');
77086
77087                 tooltip
77088                     .append('div')
77089                     .attr('class', 'popover-arrow');
77090
77091                 tooltip
77092                     .append('div')
77093                     .attr('class', 'popover-inner');
77094
77095                 resize();
77096
77097
77098                 function resize() {
77099                     surface
77100                         .attr('width', containerNode.clientWidth)
77101                         .attr('height', containerNode.clientHeight);
77102                     curtain.cut(darkness.datum());
77103                 }
77104             }
77105
77106
77107             /**
77108              * Reveal cuts the curtain to highlight the given box,
77109              * and shows a tooltip with instructions next to the box.
77110              *
77111              * @param  {String|ClientRect} [box]   box used to cut the curtain
77112              * @param  {String}    [text]          text for a tooltip
77113              * @param  {Object}    [options]
77114              * @param  {string}    [options.tooltipClass]    optional class to add to the tooltip
77115              * @param  {integer}   [options.duration]        transition time in milliseconds
77116              * @param  {string}    [options.buttonText]      if set, create a button with this text label
77117              * @param  {function}  [options.buttonCallback]  if set, the callback for the button
77118              * @param  {function}  [options.padding]         extra margin in px to put around bbox
77119              * @param  {String|ClientRect} [options.tooltipBox]  box for tooltip position, if different from box for the curtain
77120              */
77121             curtain.reveal = function(box, text, options) {
77122                 options = options || {};
77123
77124                 if (typeof box === 'string') {
77125                     box = select(box).node();
77126                 }
77127                 if (box && box.getBoundingClientRect) {
77128                     box = copyBox(box.getBoundingClientRect());
77129                     var containerRect = containerNode.getBoundingClientRect();
77130                     box.top -= containerRect.top;
77131                     box.left -= containerRect.left;
77132                 }
77133                 if (box && options.padding) {
77134                     box.top -= options.padding;
77135                     box.left -= options.padding;
77136                     box.bottom += options.padding;
77137                     box.right += options.padding;
77138                     box.height += options.padding * 2;
77139                     box.width += options.padding * 2;
77140                 }
77141
77142                 var tooltipBox;
77143                 if (options.tooltipBox) {
77144                     tooltipBox = options.tooltipBox;
77145                     if (typeof tooltipBox === 'string') {
77146                         tooltipBox = select(tooltipBox).node();
77147                     }
77148                     if (tooltipBox && tooltipBox.getBoundingClientRect) {
77149                         tooltipBox = copyBox(tooltipBox.getBoundingClientRect());
77150                     }
77151                 } else {
77152                     tooltipBox = box;
77153                 }
77154
77155                 if (tooltipBox && text) {
77156                     // pseudo markdown bold text for the instruction section..
77157                     var parts = text.split('**');
77158                     var html = parts[0] ? '<span>' + parts[0] + '</span>' : '';
77159                     if (parts[1]) {
77160                         html += '<span class="instruction">' + parts[1] + '</span>';
77161                     }
77162
77163                     html = html.replace(/\*(.*?)\*/g, '<em>$1</em>');   // emphasis
77164                     html = html.replace(/\{br\}/g, '<br/><br/>');       // linebreak
77165
77166                     if (options.buttonText && options.buttonCallback) {
77167                         html += '<div class="button-section">' +
77168                             '<button href="#" class="button action">' + options.buttonText + '</button></div>';
77169                     }
77170
77171                     var classes = 'curtain-tooltip popover tooltip arrowed in ' + (options.tooltipClass || '');
77172                     tooltip
77173                         .classed(classes, true)
77174                         .selectAll('.popover-inner')
77175                         .html(html);
77176
77177                     if (options.buttonText && options.buttonCallback) {
77178                         var button = tooltip.selectAll('.button-section .button.action');
77179                         button
77180                             .on('click', function() {
77181                                 event.preventDefault();
77182                                 options.buttonCallback();
77183                             });
77184                     }
77185
77186                     var tip = copyBox(tooltip.node().getBoundingClientRect()),
77187                         w = containerNode.clientWidth,
77188                         h = containerNode.clientHeight,
77189                         tooltipWidth = 200,
77190                         tooltipArrow = 5,
77191                         side, pos;
77192
77193
77194                     // hack: this will have bottom placement,
77195                     // so need to reserve extra space for the tooltip illustration.
77196                     if (options.tooltipClass === 'intro-mouse') {
77197                         tip.height += 80;
77198                     }
77199
77200                     // trim box dimensions to just the portion that fits in the container..
77201                     if (tooltipBox.top + tooltipBox.height > h) {
77202                         tooltipBox.height -= (tooltipBox.top + tooltipBox.height - h);
77203                     }
77204                     if (tooltipBox.left + tooltipBox.width > w) {
77205                         tooltipBox.width -= (tooltipBox.left + tooltipBox.width - w);
77206                     }
77207
77208                     // determine tooltip placement..
77209
77210                     if (tooltipBox.top + tooltipBox.height < 100) {
77211                         // tooltip below box..
77212                         side = 'bottom';
77213                         pos = [
77214                             tooltipBox.left + tooltipBox.width / 2 - tip.width / 2,
77215                             tooltipBox.top + tooltipBox.height
77216                         ];
77217
77218                     } else if (tooltipBox.top > h - 140) {
77219                         // tooltip above box..
77220                         side = 'top';
77221                         pos = [
77222                             tooltipBox.left + tooltipBox.width / 2 - tip.width / 2,
77223                             tooltipBox.top - tip.height
77224                         ];
77225
77226                     } else {
77227                         // tooltip to the side of the tooltipBox..
77228                         var tipY = tooltipBox.top + tooltipBox.height / 2 - tip.height / 2;
77229
77230                         if (_mainLocalizer.textDirection() === 'rtl') {
77231                             if (tooltipBox.left - tooltipWidth - tooltipArrow < 70) {
77232                                 side = 'right';
77233                                 pos = [tooltipBox.left + tooltipBox.width + tooltipArrow, tipY];
77234
77235                             } else {
77236                                 side = 'left';
77237                                 pos = [tooltipBox.left - tooltipWidth - tooltipArrow, tipY];
77238                             }
77239
77240                         } else {
77241                             if (tooltipBox.left + tooltipBox.width + tooltipArrow + tooltipWidth > w - 70) {
77242                                 side = 'left';
77243                                 pos = [tooltipBox.left - tooltipWidth - tooltipArrow, tipY];
77244                             }
77245                             else {
77246                                 side = 'right';
77247                                 pos = [tooltipBox.left + tooltipBox.width + tooltipArrow, tipY];
77248                             }
77249                         }
77250                     }
77251
77252                     if (options.duration !== 0 || !tooltip.classed(side)) {
77253                         tooltip.call(uiToggle(true));
77254                     }
77255
77256                     tooltip
77257                         .style('top', pos[1] + 'px')
77258                         .style('left', pos[0] + 'px')
77259                         .attr('class', classes + ' ' + side);
77260
77261
77262                     // shift popover-inner if it is very close to the top or bottom edge
77263                     // (doesn't affect the placement of the popover-arrow)
77264                     var shiftY = 0;
77265                     if (side === 'left' || side === 'right') {
77266                         if (pos[1] < 60) {
77267                             shiftY = 60 - pos[1];
77268                         }
77269                         else if (pos[1] + tip.height > h - 100) {
77270                             shiftY = h - pos[1] - tip.height - 100;
77271                         }
77272                     }
77273                     tooltip.selectAll('.popover-inner')
77274                         .style('top', shiftY + 'px');
77275
77276                 } else {
77277                     tooltip
77278                         .classed('in', false)
77279                         .call(uiToggle(false));
77280                 }
77281
77282                 curtain.cut(box, options.duration);
77283
77284                 return tooltip;
77285             };
77286
77287
77288             curtain.cut = function(datum, duration) {
77289                 darkness.datum(datum)
77290                     .interrupt();
77291
77292                 var selection;
77293                 if (duration === 0) {
77294                     selection = darkness;
77295                 } else {
77296                     selection = darkness
77297                         .transition()
77298                         .duration(duration || 600)
77299                         .ease(linear$1);
77300                 }
77301
77302                 selection
77303                     .attr('d', function(d) {
77304                         var containerWidth = containerNode.clientWidth;
77305                         var containerHeight = containerNode.clientHeight;
77306                         var string = 'M 0,0 L 0,' + containerHeight + ' L ' +
77307                             containerWidth + ',' + containerHeight + 'L' +
77308                             containerWidth + ',0 Z';
77309
77310                         if (!d) { return string; }
77311                         return string + 'M' +
77312                             d.left + ',' + d.top + 'L' +
77313                             d.left + ',' + (d.top + d.height) + 'L' +
77314                             (d.left + d.width) + ',' + (d.top + d.height) + 'L' +
77315                             (d.left + d.width) + ',' + (d.top) + 'Z';
77316
77317                     });
77318             };
77319
77320
77321             curtain.remove = function() {
77322                 surface.remove();
77323                 tooltip.remove();
77324                 select(window).on('resize.curtain', null);
77325             };
77326
77327
77328             // ClientRects are immutable, so copy them to an object,
77329             // in case we need to trim the height/width.
77330             function copyBox(src) {
77331                 return {
77332                     top: src.top,
77333                     right: src.right,
77334                     bottom: src.bottom,
77335                     left: src.left,
77336                     width: src.width,
77337                     height: src.height
77338                 };
77339             }
77340
77341
77342             return curtain;
77343         }
77344
77345         function uiIntroWelcome(context, reveal) {
77346             var dispatch$1 = dispatch('done');
77347
77348             var chapter = {
77349                 title: 'intro.welcome.title'
77350             };
77351
77352
77353             function welcome() {
77354                 context.map().centerZoom([-85.63591, 41.94285], 19);
77355                 reveal('.intro-nav-wrap .chapter-welcome',
77356                     helpString('intro.welcome.welcome'),
77357                     { buttonText: _t('intro.ok'), buttonCallback: practice }
77358                 );
77359             }
77360
77361             function practice() {
77362                 reveal('.intro-nav-wrap .chapter-welcome',
77363                     helpString('intro.welcome.practice'),
77364                     { buttonText: _t('intro.ok'), buttonCallback: words }
77365                 );
77366             }
77367
77368             function words() {
77369                 reveal('.intro-nav-wrap .chapter-welcome',
77370                     helpString('intro.welcome.words'),
77371                     { buttonText: _t('intro.ok'), buttonCallback: chapters }
77372                 );
77373             }
77374
77375
77376             function chapters() {
77377                 dispatch$1.call('done');
77378                 reveal('.intro-nav-wrap .chapter-navigation',
77379                     helpString('intro.welcome.chapters', { next: _t('intro.navigation.title') })
77380                 );
77381             }
77382
77383
77384             chapter.enter = function() {
77385                 welcome();
77386             };
77387
77388
77389             chapter.exit = function() {
77390                 context.container().select('.curtain-tooltip.intro-mouse')
77391                     .selectAll('.counter')
77392                     .remove();
77393             };
77394
77395
77396             chapter.restart = function() {
77397                 chapter.exit();
77398                 chapter.enter();
77399             };
77400
77401
77402             return utilRebind(chapter, dispatch$1, 'on');
77403         }
77404
77405         function uiIntroNavigation(context, reveal) {
77406             var dispatch$1 = dispatch('done');
77407             var timeouts = [];
77408             var hallId = 'n2061';
77409             var townHall = [-85.63591, 41.94285];
77410             var springStreetId = 'w397';
77411             var springStreetEndId = 'n1834';
77412             var springStreet = [-85.63582, 41.94255];
77413             var onewayField = _mainPresetIndex.field('oneway');
77414             var maxspeedField = _mainPresetIndex.field('maxspeed');
77415
77416
77417             var chapter = {
77418                 title: 'intro.navigation.title'
77419             };
77420
77421
77422             function timeout(f, t) {
77423                 timeouts.push(window.setTimeout(f, t));
77424             }
77425
77426
77427             function eventCancel() {
77428                 event.stopPropagation();
77429                 event.preventDefault();
77430             }
77431
77432
77433             function isTownHallSelected() {
77434                 var ids = context.selectedIDs();
77435                 return ids.length === 1 && ids[0] === hallId;
77436             }
77437
77438
77439             function dragMap() {
77440                 context.enter(modeBrowse(context));
77441                 context.history().reset('initial');
77442
77443                 var msec = transitionTime(townHall, context.map().center());
77444                 if (msec) { reveal(null, null, { duration: 0 }); }
77445                 context.map().centerZoomEase(townHall, 19, msec);
77446
77447                 timeout(function() {
77448                     var centerStart = context.map().center();
77449
77450                     var textId = context.lastPointerType() === 'mouse' ? 'drag' : 'drag_touch';
77451                     var dragString = helpString('intro.navigation.map_info') + '{br}' + helpString('intro.navigation.' + textId);
77452                     reveal('.surface', dragString);
77453                     context.map().on('drawn.intro', function() {
77454                         reveal('.surface', dragString, { duration: 0 });
77455                     });
77456
77457                     context.map().on('move.intro', function() {
77458                         var centerNow = context.map().center();
77459                         if (centerStart[0] !== centerNow[0] || centerStart[1] !== centerNow[1]) {
77460                             context.map().on('move.intro', null);
77461                             timeout(function() { continueTo(zoomMap); }, 3000);
77462                         }
77463                     });
77464
77465                 }, msec + 100);
77466
77467                 function continueTo(nextStep) {
77468                     context.map().on('move.intro drawn.intro', null);
77469                     nextStep();
77470                 }
77471             }
77472
77473
77474             function zoomMap() {
77475                 var zoomStart = context.map().zoom();
77476
77477                 var textId = context.lastPointerType() === 'mouse' ? 'zoom' : 'zoom_touch';
77478                 var zoomString = helpString('intro.navigation.' + textId);
77479
77480                 reveal('.surface', zoomString);
77481
77482                 context.map().on('drawn.intro', function() {
77483                     reveal('.surface', zoomString, { duration: 0 });
77484                 });
77485
77486                 context.map().on('move.intro', function() {
77487                     if (context.map().zoom() !== zoomStart) {
77488                         context.map().on('move.intro', null);
77489                         timeout(function() { continueTo(features); }, 3000);
77490                     }
77491                 });
77492
77493                 function continueTo(nextStep) {
77494                     context.map().on('move.intro drawn.intro', null);
77495                     nextStep();
77496                 }
77497             }
77498
77499
77500             function features() {
77501                 var onClick = function() { continueTo(pointsLinesAreas); };
77502
77503                 reveal('.surface', helpString('intro.navigation.features'),
77504                     { buttonText: _t('intro.ok'), buttonCallback: onClick }
77505                 );
77506
77507                 context.map().on('drawn.intro', function() {
77508                     reveal('.surface', helpString('intro.navigation.features'),
77509                         { duration: 0, buttonText: _t('intro.ok'), buttonCallback: onClick }
77510                     );
77511                 });
77512
77513                 function continueTo(nextStep) {
77514                     context.map().on('drawn.intro', null);
77515                     nextStep();
77516                 }
77517             }
77518
77519             function pointsLinesAreas() {
77520                 var onClick = function() { continueTo(nodesWays); };
77521
77522                 reveal('.surface', helpString('intro.navigation.points_lines_areas'),
77523                     { buttonText: _t('intro.ok'), buttonCallback: onClick }
77524                 );
77525
77526                 context.map().on('drawn.intro', function() {
77527                     reveal('.surface', helpString('intro.navigation.points_lines_areas'),
77528                         { duration: 0, buttonText: _t('intro.ok'), buttonCallback: onClick }
77529                     );
77530                 });
77531
77532                 function continueTo(nextStep) {
77533                     context.map().on('drawn.intro', null);
77534                     nextStep();
77535                 }
77536             }
77537
77538             function nodesWays() {
77539                 var onClick = function() { continueTo(clickTownHall); };
77540
77541                 reveal('.surface', helpString('intro.navigation.nodes_ways'),
77542                     { buttonText: _t('intro.ok'), buttonCallback: onClick }
77543                 );
77544
77545                 context.map().on('drawn.intro', function() {
77546                     reveal('.surface', helpString('intro.navigation.nodes_ways'),
77547                         { duration: 0, buttonText: _t('intro.ok'), buttonCallback: onClick }
77548                     );
77549                 });
77550
77551                 function continueTo(nextStep) {
77552                     context.map().on('drawn.intro', null);
77553                     nextStep();
77554                 }
77555             }
77556
77557             function clickTownHall() {
77558                 context.enter(modeBrowse(context));
77559                 context.history().reset('initial');
77560
77561                 var entity = context.hasEntity(hallId);
77562                 if (!entity) { return; }
77563                 reveal(null, null, { duration: 0 });
77564                 context.map().centerZoomEase(entity.loc, 19, 500);
77565
77566                 timeout(function() {
77567                     var entity = context.hasEntity(hallId);
77568                     if (!entity) { return; }
77569                     var box = pointBox(entity.loc, context);
77570                     var textId = context.lastPointerType() === 'mouse' ? 'click_townhall' : 'tap_townhall';
77571                     reveal(box, helpString('intro.navigation.' + textId));
77572
77573                     context.map().on('move.intro drawn.intro', function() {
77574                         var entity = context.hasEntity(hallId);
77575                         if (!entity) { return; }
77576                         var box = pointBox(entity.loc, context);
77577                         reveal(box, helpString('intro.navigation.' + textId), { duration: 0 });
77578                     });
77579
77580                     context.on('enter.intro', function() {
77581                         if (isTownHallSelected()) { continueTo(selectedTownHall); }
77582                     });
77583
77584                 }, 550);  // after centerZoomEase
77585
77586                 context.history().on('change.intro', function() {
77587                     if (!context.hasEntity(hallId)) {
77588                         continueTo(clickTownHall);
77589                     }
77590                 });
77591
77592                 function continueTo(nextStep) {
77593                     context.on('enter.intro', null);
77594                     context.map().on('move.intro drawn.intro', null);
77595                     context.history().on('change.intro', null);
77596                     nextStep();
77597                 }
77598             }
77599
77600
77601             function selectedTownHall() {
77602                 if (!isTownHallSelected()) { return clickTownHall(); }
77603
77604                 var entity = context.hasEntity(hallId);
77605                 if (!entity) { return clickTownHall(); }
77606
77607                 var box = pointBox(entity.loc, context);
77608                 var onClick = function() { continueTo(editorTownHall); };
77609
77610                 reveal(box, helpString('intro.navigation.selected_townhall'),
77611                     { buttonText: _t('intro.ok'), buttonCallback: onClick }
77612                 );
77613
77614                 context.map().on('move.intro drawn.intro', function() {
77615                     var entity = context.hasEntity(hallId);
77616                     if (!entity) { return; }
77617                     var box = pointBox(entity.loc, context);
77618                     reveal(box, helpString('intro.navigation.selected_townhall'),
77619                         { duration: 0, buttonText: _t('intro.ok'), buttonCallback: onClick }
77620                     );
77621                 });
77622
77623                 context.history().on('change.intro', function() {
77624                     if (!context.hasEntity(hallId)) {
77625                         continueTo(clickTownHall);
77626                     }
77627                 });
77628
77629                 function continueTo(nextStep) {
77630                     context.map().on('move.intro drawn.intro', null);
77631                     context.history().on('change.intro', null);
77632                     nextStep();
77633                 }
77634             }
77635
77636
77637             function editorTownHall() {
77638                 if (!isTownHallSelected()) { return clickTownHall(); }
77639
77640                 // disallow scrolling
77641                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
77642
77643                 var onClick = function() { continueTo(presetTownHall); };
77644
77645                 reveal('.entity-editor-pane',
77646                     helpString('intro.navigation.editor_townhall'),
77647                     { buttonText: _t('intro.ok'), buttonCallback: onClick }
77648                 );
77649
77650                 context.on('exit.intro', function() {
77651                     continueTo(clickTownHall);
77652                 });
77653
77654                 context.history().on('change.intro', function() {
77655                     if (!context.hasEntity(hallId)) {
77656                         continueTo(clickTownHall);
77657                     }
77658                 });
77659
77660                 function continueTo(nextStep) {
77661                     context.on('exit.intro', null);
77662                     context.history().on('change.intro', null);
77663                     context.container().select('.inspector-wrap').on('wheel.intro', null);
77664                     nextStep();
77665                 }
77666             }
77667
77668
77669             function presetTownHall() {
77670                 if (!isTownHallSelected()) { return clickTownHall(); }
77671
77672                 // reset pane, in case user happened to change it..
77673                 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
77674                 // disallow scrolling
77675                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
77676
77677                 // preset match, in case the user happened to change it.
77678                 var entity = context.entity(context.selectedIDs()[0]);
77679                 var preset = _mainPresetIndex.match(entity, context.graph());
77680
77681                 var onClick = function() { continueTo(fieldsTownHall); };
77682
77683                 reveal('.entity-editor-pane .section-feature-type',
77684                     helpString('intro.navigation.preset_townhall', { preset: preset.name() }),
77685                     { buttonText: _t('intro.ok'), buttonCallback: onClick }
77686                 );
77687
77688                 context.on('exit.intro', function() {
77689                     continueTo(clickTownHall);
77690                 });
77691
77692                 context.history().on('change.intro', function() {
77693                     if (!context.hasEntity(hallId)) {
77694                         continueTo(clickTownHall);
77695                     }
77696                 });
77697
77698                 function continueTo(nextStep) {
77699                     context.on('exit.intro', null);
77700                     context.history().on('change.intro', null);
77701                     context.container().select('.inspector-wrap').on('wheel.intro', null);
77702                     nextStep();
77703                 }
77704             }
77705
77706
77707             function fieldsTownHall() {
77708                 if (!isTownHallSelected()) { return clickTownHall(); }
77709
77710                 // reset pane, in case user happened to change it..
77711                 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
77712                 // disallow scrolling
77713                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
77714
77715                 var onClick = function() { continueTo(closeTownHall); };
77716
77717                 reveal('.entity-editor-pane .section-preset-fields',
77718                     helpString('intro.navigation.fields_townhall'),
77719                     { buttonText: _t('intro.ok'), buttonCallback: onClick }
77720                 );
77721
77722                 context.on('exit.intro', function() {
77723                     continueTo(clickTownHall);
77724                 });
77725
77726                 context.history().on('change.intro', function() {
77727                     if (!context.hasEntity(hallId)) {
77728                         continueTo(clickTownHall);
77729                     }
77730                 });
77731
77732                 function continueTo(nextStep) {
77733                     context.on('exit.intro', null);
77734                     context.history().on('change.intro', null);
77735                     context.container().select('.inspector-wrap').on('wheel.intro', null);
77736                     nextStep();
77737                 }
77738             }
77739
77740
77741             function closeTownHall() {
77742                 if (!isTownHallSelected()) { return clickTownHall(); }
77743
77744                 var selector = '.entity-editor-pane button.close svg use';
77745                 var href = select(selector).attr('href') || '#iD-icon-close';
77746
77747                 reveal('.entity-editor-pane',
77748                     helpString('intro.navigation.close_townhall', { button: icon(href, 'pre-text') })
77749                 );
77750
77751                 context.on('exit.intro', function() {
77752                     continueTo(searchStreet);
77753                 });
77754
77755                 context.history().on('change.intro', function() {
77756                     // update the close icon in the tooltip if the user edits something.
77757                     var selector = '.entity-editor-pane button.close svg use';
77758                     var href = select(selector).attr('href') || '#iD-icon-close';
77759
77760                     reveal('.entity-editor-pane',
77761                         helpString('intro.navigation.close_townhall', { button: icon(href, 'pre-text') }),
77762                         { duration: 0 }
77763                     );
77764                 });
77765
77766                 function continueTo(nextStep) {
77767                     context.on('exit.intro', null);
77768                     context.history().on('change.intro', null);
77769                     nextStep();
77770                 }
77771             }
77772
77773
77774             function searchStreet() {
77775                 context.enter(modeBrowse(context));
77776                 context.history().reset('initial');  // ensure spring street exists
77777
77778                 var msec = transitionTime(springStreet, context.map().center());
77779                 if (msec) { reveal(null, null, { duration: 0 }); }
77780                 context.map().centerZoomEase(springStreet, 19, msec);  // ..and user can see it
77781
77782                 timeout(function() {
77783                     reveal('.search-header input',
77784                         helpString('intro.navigation.search_street', { name: _t('intro.graph.name.spring-street') })
77785                     );
77786
77787                     context.container().select('.search-header input')
77788                         .on('keyup.intro', checkSearchResult);
77789                 }, msec + 100);
77790             }
77791
77792
77793             function checkSearchResult() {
77794                 var first = context.container().select('.feature-list-item:nth-child(0n+2)');  // skip "No Results" item
77795                 var firstName = first.select('.entity-name');
77796                 var name = _t('intro.graph.name.spring-street');
77797
77798                 if (!firstName.empty() && firstName.text() === name) {
77799                     reveal(first.node(),
77800                         helpString('intro.navigation.choose_street', { name: name }),
77801                         { duration: 300 }
77802                     );
77803
77804                     context.on('exit.intro', function() {
77805                         continueTo(selectedStreet);
77806                     });
77807
77808                     context.container().select('.search-header input')
77809                         .on('keydown.intro', eventCancel, true)
77810                         .on('keyup.intro', null);
77811                 }
77812
77813                 function continueTo(nextStep) {
77814                     context.on('exit.intro', null);
77815                     context.container().select('.search-header input')
77816                         .on('keydown.intro', null)
77817                         .on('keyup.intro', null);
77818                     nextStep();
77819                 }
77820             }
77821
77822
77823             function selectedStreet() {
77824                 if (!context.hasEntity(springStreetEndId) || !context.hasEntity(springStreetId)) {
77825                     return searchStreet();
77826                 }
77827
77828                 var onClick = function() { continueTo(editorStreet); };
77829                 var entity = context.entity(springStreetEndId);
77830                 var box = pointBox(entity.loc, context);
77831                 box.height = 500;
77832
77833                 reveal(box,
77834                     helpString('intro.navigation.selected_street', { name: _t('intro.graph.name.spring-street') }),
77835                     { duration: 600, buttonText: _t('intro.ok'), buttonCallback: onClick }
77836                 );
77837
77838                 timeout(function() {
77839                     context.map().on('move.intro drawn.intro', function() {
77840                         var entity = context.hasEntity(springStreetEndId);
77841                         if (!entity) { return; }
77842                         var box = pointBox(entity.loc, context);
77843                         box.height = 500;
77844                         reveal(box,
77845                             helpString('intro.navigation.selected_street', { name: _t('intro.graph.name.spring-street') }),
77846                             { duration: 0, buttonText: _t('intro.ok'), buttonCallback: onClick }
77847                         );
77848                     });
77849                 }, 600);  // after reveal.
77850
77851                 context.on('enter.intro', function(mode) {
77852                     if (!context.hasEntity(springStreetId)) {
77853                         return continueTo(searchStreet);
77854                     }
77855                     var ids = context.selectedIDs();
77856                     if (mode.id !== 'select' || !ids.length || ids[0] !== springStreetId) {
77857                         // keep Spring Street selected..
77858                         context.enter(modeSelect(context, [springStreetId]));
77859                     }
77860                 });
77861
77862                 context.history().on('change.intro', function() {
77863                     if (!context.hasEntity(springStreetEndId) || !context.hasEntity(springStreetId)) {
77864                         timeout(function() {
77865                             continueTo(searchStreet);
77866                         }, 300);  // after any transition (e.g. if user deleted intersection)
77867                     }
77868                 });
77869
77870                 function continueTo(nextStep) {
77871                     context.map().on('move.intro drawn.intro', null);
77872                     context.on('enter.intro', null);
77873                     context.history().on('change.intro', null);
77874                     nextStep();
77875                 }
77876             }
77877
77878
77879             function editorStreet() {
77880                 var selector = '.entity-editor-pane button.close svg use';
77881                 var href = select(selector).attr('href') || '#iD-icon-close';
77882
77883                 reveal('.entity-editor-pane', helpString('intro.navigation.street_different_fields') + '{br}' +
77884                     helpString('intro.navigation.editor_street', {
77885                         button: icon(href, 'pre-text'),
77886                         field1: onewayField.label(),
77887                         field2: maxspeedField.label()
77888                     }));
77889
77890                 context.on('exit.intro', function() {
77891                     continueTo(play);
77892                 });
77893
77894                 context.history().on('change.intro', function() {
77895                     // update the close icon in the tooltip if the user edits something.
77896                     var selector = '.entity-editor-pane button.close svg use';
77897                     var href = select(selector).attr('href') || '#iD-icon-close';
77898
77899                     reveal('.entity-editor-pane', helpString('intro.navigation.street_different_fields') + '{br}' +
77900                         helpString('intro.navigation.editor_street', {
77901                             button: icon(href, 'pre-text'),
77902                             field1: onewayField.label(),
77903                             field2: maxspeedField.label()
77904                         }), { duration: 0 }
77905                     );
77906                 });
77907
77908                 function continueTo(nextStep) {
77909                     context.on('exit.intro', null);
77910                     context.history().on('change.intro', null);
77911                     nextStep();
77912                 }
77913             }
77914
77915
77916             function play() {
77917                 dispatch$1.call('done');
77918                 reveal('.ideditor',
77919                     helpString('intro.navigation.play', { next: _t('intro.points.title') }), {
77920                         tooltipBox: '.intro-nav-wrap .chapter-point',
77921                         buttonText: _t('intro.ok'),
77922                         buttonCallback: function() { reveal('.ideditor'); }
77923                     }
77924                 );
77925             }
77926
77927
77928             chapter.enter = function() {
77929                 dragMap();
77930             };
77931
77932
77933             chapter.exit = function() {
77934                 timeouts.forEach(window.clearTimeout);
77935                 context.on('enter.intro exit.intro', null);
77936                 context.map().on('move.intro drawn.intro', null);
77937                 context.history().on('change.intro', null);
77938                 context.container().select('.inspector-wrap').on('wheel.intro', null);
77939                 context.container().select('.search-header input').on('keydown.intro keyup.intro', null);
77940             };
77941
77942
77943             chapter.restart = function() {
77944                 chapter.exit();
77945                 chapter.enter();
77946             };
77947
77948
77949             return utilRebind(chapter, dispatch$1, 'on');
77950         }
77951
77952         function uiIntroPoint(context, reveal) {
77953             var dispatch$1 = dispatch('done');
77954             var timeouts = [];
77955             var intersection = [-85.63279, 41.94394];
77956             var building = [-85.632422, 41.944045];
77957             var cafePreset = _mainPresetIndex.item('amenity/cafe');
77958             var _pointID = null;
77959
77960
77961             var chapter = {
77962                 title: 'intro.points.title'
77963             };
77964
77965
77966             function timeout(f, t) {
77967                 timeouts.push(window.setTimeout(f, t));
77968             }
77969
77970
77971             function eventCancel() {
77972                 event.stopPropagation();
77973                 event.preventDefault();
77974             }
77975
77976
77977             function addPoint() {
77978                 context.enter(modeBrowse(context));
77979                 context.history().reset('initial');
77980
77981                 var msec = transitionTime(intersection, context.map().center());
77982                 if (msec) { reveal(null, null, { duration: 0 }); }
77983                 context.map().centerZoomEase(intersection, 19, msec);
77984
77985                 timeout(function() {
77986                     var tooltip = reveal('button.add-point',
77987                         helpString('intro.points.points_info') + '{br}' + helpString('intro.points.add_point'));
77988
77989                     _pointID = null;
77990
77991                     tooltip.selectAll('.popover-inner')
77992                         .insert('svg', 'span')
77993                         .attr('class', 'tooltip-illustration')
77994                         .append('use')
77995                         .attr('xlink:href', '#iD-graphic-points');
77996
77997                     context.on('enter.intro', function(mode) {
77998                         if (mode.id !== 'add-point') { return; }
77999                         continueTo(placePoint);
78000                     });
78001                 }, msec + 100);
78002
78003                 function continueTo(nextStep) {
78004                     context.on('enter.intro', null);
78005                     nextStep();
78006                 }
78007             }
78008
78009
78010             function placePoint() {
78011                 if (context.mode().id !== 'add-point') {
78012                     return chapter.restart();
78013                 }
78014
78015                 var pointBox = pad(building, 150, context);
78016                 var textId = context.lastPointerType() === 'mouse' ? 'place_point' : 'place_point_touch';
78017                 reveal(pointBox, helpString('intro.points.' + textId));
78018
78019                 context.map().on('move.intro drawn.intro', function() {
78020                     pointBox = pad(building, 150, context);
78021                     reveal(pointBox, helpString('intro.points.' + textId), { duration: 0 });
78022                 });
78023
78024                 context.on('enter.intro', function(mode) {
78025                     if (mode.id !== 'select') { return chapter.restart(); }
78026                     _pointID = context.mode().selectedIDs()[0];
78027                     continueTo(searchPreset);
78028                 });
78029
78030                 function continueTo(nextStep) {
78031                     context.map().on('move.intro drawn.intro', null);
78032                     context.on('enter.intro', null);
78033                     nextStep();
78034                 }
78035             }
78036
78037
78038             function searchPreset() {
78039                 if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
78040                     return addPoint();
78041                 }
78042
78043                 // disallow scrolling
78044                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
78045
78046                 context.container().select('.preset-search-input')
78047                     .on('keydown.intro', null)
78048                     .on('keyup.intro', checkPresetSearch);
78049
78050                 reveal('.preset-search-input',
78051                     helpString('intro.points.search_cafe', { preset: cafePreset.name() })
78052                 );
78053
78054                 context.on('enter.intro', function(mode) {
78055                     if (!_pointID || !context.hasEntity(_pointID)) {
78056                         return continueTo(addPoint);
78057                     }
78058
78059                     var ids = context.selectedIDs();
78060                     if (mode.id !== 'select' || !ids.length || ids[0] !== _pointID) {
78061                         // keep the user's point selected..
78062                         context.enter(modeSelect(context, [_pointID]));
78063
78064                         // disallow scrolling
78065                         context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
78066
78067                         context.container().select('.preset-search-input')
78068                             .on('keydown.intro', null)
78069                             .on('keyup.intro', checkPresetSearch);
78070
78071                         reveal('.preset-search-input',
78072                             helpString('intro.points.search_cafe', { preset: cafePreset.name() })
78073                         );
78074
78075                         context.history().on('change.intro', null);
78076                     }
78077                 });
78078
78079
78080                 function checkPresetSearch() {
78081                     var first = context.container().select('.preset-list-item:first-child');
78082
78083                     if (first.classed('preset-amenity-cafe')) {
78084                         context.container().select('.preset-search-input')
78085                             .on('keydown.intro', eventCancel, true)
78086                             .on('keyup.intro', null);
78087
78088                         reveal(first.select('.preset-list-button').node(),
78089                             helpString('intro.points.choose_cafe', { preset: cafePreset.name() }),
78090                             { duration: 300 }
78091                         );
78092
78093                         context.history().on('change.intro', function() {
78094                             continueTo(aboutFeatureEditor);
78095                         });
78096                     }
78097                 }
78098
78099                 function continueTo(nextStep) {
78100                     context.on('enter.intro', null);
78101                     context.history().on('change.intro', null);
78102                     context.container().select('.inspector-wrap').on('wheel.intro', null);
78103                     context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
78104                     nextStep();
78105                 }
78106             }
78107
78108
78109             function aboutFeatureEditor() {
78110                 if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
78111                     return addPoint();
78112                 }
78113
78114                 timeout(function() {
78115                     reveal('.entity-editor-pane', helpString('intro.points.feature_editor'), {
78116                         tooltipClass: 'intro-points-describe',
78117                         buttonText: _t('intro.ok'),
78118                         buttonCallback: function() { continueTo(addName); }
78119                     });
78120                 }, 400);
78121
78122                 context.on('exit.intro', function() {
78123                     // if user leaves select mode here, just continue with the tutorial.
78124                     continueTo(reselectPoint);
78125                 });
78126
78127                 function continueTo(nextStep) {
78128                     context.on('exit.intro', null);
78129                     nextStep();
78130                 }
78131             }
78132
78133
78134             function addName() {
78135                 if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
78136                     return addPoint();
78137                 }
78138
78139                 // reset pane, in case user happened to change it..
78140                 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
78141
78142                 var addNameString = helpString('intro.points.fields_info') + '{br}' + helpString('intro.points.add_name');
78143
78144                 timeout(function() {
78145                     // It's possible for the user to add a name in a previous step..
78146                     // If so, don't tell them to add the name in this step.
78147                     // Give them an OK button instead.
78148                     var entity = context.entity(_pointID);
78149                     if (entity.tags.name) {
78150                         var tooltip = reveal('.entity-editor-pane', addNameString, {
78151                             tooltipClass: 'intro-points-describe',
78152                             buttonText: _t('intro.ok'),
78153                             buttonCallback: function() { continueTo(addCloseEditor); }
78154                         });
78155                         tooltip.select('.instruction').style('display', 'none');
78156
78157                     } else {
78158                         reveal('.entity-editor-pane', addNameString,
78159                             { tooltipClass: 'intro-points-describe' }
78160                         );
78161                     }
78162                 }, 400);
78163
78164                 context.history().on('change.intro', function() {
78165                     continueTo(addCloseEditor);
78166                 });
78167
78168                 context.on('exit.intro', function() {
78169                     // if user leaves select mode here, just continue with the tutorial.
78170                     continueTo(reselectPoint);
78171                 });
78172
78173                 function continueTo(nextStep) {
78174                     context.on('exit.intro', null);
78175                     context.history().on('change.intro', null);
78176                     nextStep();
78177                 }
78178             }
78179
78180
78181             function addCloseEditor() {
78182                 // reset pane, in case user happened to change it..
78183                 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
78184
78185                 var selector = '.entity-editor-pane button.close svg use';
78186                 var href = select(selector).attr('href') || '#iD-icon-close';
78187
78188                 context.on('exit.intro', function() {
78189                     continueTo(reselectPoint);
78190                 });
78191
78192                 reveal('.entity-editor-pane',
78193                     helpString('intro.points.add_close', { button: icon(href, 'pre-text') })
78194                 );
78195
78196                 function continueTo(nextStep) {
78197                     context.on('exit.intro', null);
78198                     nextStep();
78199                 }
78200             }
78201
78202
78203             function reselectPoint() {
78204                 if (!_pointID) { return chapter.restart(); }
78205                 var entity = context.hasEntity(_pointID);
78206                 if (!entity) { return chapter.restart(); }
78207
78208                 // make sure it's still a cafe, in case user somehow changed it..
78209                 var oldPreset = _mainPresetIndex.match(entity, context.graph());
78210                 context.replace(actionChangePreset(_pointID, oldPreset, cafePreset));
78211
78212                 context.enter(modeBrowse(context));
78213
78214                 var msec = transitionTime(entity.loc, context.map().center());
78215                 if (msec) { reveal(null, null, { duration: 0 }); }
78216                 context.map().centerEase(entity.loc, msec);
78217
78218                 timeout(function() {
78219                     var box = pointBox(entity.loc, context);
78220                     reveal(box, helpString('intro.points.reselect'), { duration: 600 });
78221
78222                     timeout(function() {
78223                         context.map().on('move.intro drawn.intro', function() {
78224                             var entity = context.hasEntity(_pointID);
78225                             if (!entity) { return chapter.restart(); }
78226                             var box = pointBox(entity.loc, context);
78227                             reveal(box, helpString('intro.points.reselect'), { duration: 0 });
78228                         });
78229                     }, 600); // after reveal..
78230
78231                     context.on('enter.intro', function(mode) {
78232                         if (mode.id !== 'select') { return; }
78233                         continueTo(updatePoint);
78234                     });
78235
78236                 }, msec + 100);
78237
78238                 function continueTo(nextStep) {
78239                     context.map().on('move.intro drawn.intro', null);
78240                     context.on('enter.intro', null);
78241                     nextStep();
78242                 }
78243             }
78244
78245
78246             function updatePoint() {
78247                 if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
78248                     return continueTo(reselectPoint);
78249                 }
78250
78251                 // reset pane, in case user happened to untag the point..
78252                 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
78253
78254                 context.on('exit.intro', function() {
78255                     continueTo(reselectPoint);
78256                 });
78257
78258                 context.history().on('change.intro', function() {
78259                     continueTo(updateCloseEditor);
78260                 });
78261
78262                 timeout(function() {
78263                     reveal('.entity-editor-pane', helpString('intro.points.update'),
78264                         { tooltipClass: 'intro-points-describe' }
78265                     );
78266                 }, 400);
78267
78268                 function continueTo(nextStep) {
78269                     context.on('exit.intro', null);
78270                     context.history().on('change.intro', null);
78271                     nextStep();
78272                 }
78273             }
78274
78275
78276             function updateCloseEditor() {
78277                 if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
78278                     return continueTo(reselectPoint);
78279                 }
78280
78281                 // reset pane, in case user happened to change it..
78282                 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
78283
78284                 context.on('exit.intro', function() {
78285                     continueTo(rightClickPoint);
78286                 });
78287
78288                 timeout(function() {
78289                     reveal('.entity-editor-pane',
78290                         helpString('intro.points.update_close', { button: icon('#iD-icon-close', 'pre-text') })
78291                     );
78292                 }, 500);
78293
78294                 function continueTo(nextStep) {
78295                     context.on('exit.intro', null);
78296                     nextStep();
78297                 }
78298             }
78299
78300
78301             function rightClickPoint() {
78302                 if (!_pointID) { return chapter.restart(); }
78303                 var entity = context.hasEntity(_pointID);
78304                 if (!entity) { return chapter.restart(); }
78305
78306                 context.enter(modeBrowse(context));
78307
78308                 var box = pointBox(entity.loc, context);
78309                 var textId = context.lastPointerType() === 'mouse' ? 'rightclick' : 'edit_menu_touch';
78310                 reveal(box, helpString('intro.points.' + textId), { duration: 600 });
78311
78312                 timeout(function() {
78313                     context.map().on('move.intro', function() {
78314                         var entity = context.hasEntity(_pointID);
78315                         if (!entity) { return chapter.restart(); }
78316                         var box = pointBox(entity.loc, context);
78317                         reveal(box, helpString('intro.points.' + textId), { duration: 0 });
78318                     });
78319                 }, 600); // after reveal
78320
78321                 context.on('enter.intro', function(mode) {
78322                     if (mode.id !== 'select') { return; }
78323                     var ids = context.selectedIDs();
78324                     if (ids.length !== 1 || ids[0] !== _pointID) { return; }
78325
78326                     timeout(function() {
78327                         var node = selectMenuItem(context, 'delete').node();
78328                         if (!node) { return; }
78329                         continueTo(enterDelete);
78330                     }, 50);  // after menu visible
78331                 });
78332
78333                 function continueTo(nextStep) {
78334                     context.on('enter.intro', null);
78335                     context.map().on('move.intro', null);
78336                     nextStep();
78337                 }
78338             }
78339
78340
78341             function enterDelete() {
78342                 if (!_pointID) { return chapter.restart(); }
78343                 var entity = context.hasEntity(_pointID);
78344                 if (!entity) { return chapter.restart(); }
78345
78346                 var node = selectMenuItem(context, 'delete').node();
78347                 if (!node) { return continueTo(rightClickPoint); }
78348
78349                 reveal('.edit-menu',
78350                     helpString('intro.points.delete'),
78351                     { padding: 50 }
78352                 );
78353
78354                 timeout(function() {
78355                     context.map().on('move.intro', function() {
78356                         reveal('.edit-menu',
78357                             helpString('intro.points.delete'),
78358                             { duration: 0,  padding: 50 }
78359                         );
78360                     });
78361                 }, 300); // after menu visible
78362
78363                 context.on('exit.intro', function() {
78364                     if (!_pointID) { return chapter.restart(); }
78365                     var entity = context.hasEntity(_pointID);
78366                     if (entity) { return continueTo(rightClickPoint); }  // point still exists
78367                 });
78368
78369                 context.history().on('change.intro', function(changed) {
78370                     if (changed.deleted().length) {
78371                         continueTo(undo);
78372                     }
78373                 });
78374
78375                 function continueTo(nextStep) {
78376                     context.map().on('move.intro', null);
78377                     context.history().on('change.intro', null);
78378                     context.on('exit.intro', null);
78379                     nextStep();
78380                 }
78381             }
78382
78383
78384             function undo() {
78385                 context.history().on('change.intro', function() {
78386                     continueTo(play);
78387                 });
78388
78389                 reveal('.top-toolbar button.undo-button',
78390                     helpString('intro.points.undo')
78391                 );
78392
78393                 function continueTo(nextStep) {
78394                     context.history().on('change.intro', null);
78395                     nextStep();
78396                 }
78397             }
78398
78399
78400             function play() {
78401                 dispatch$1.call('done');
78402                 reveal('.ideditor',
78403                     helpString('intro.points.play', { next: _t('intro.areas.title') }), {
78404                         tooltipBox: '.intro-nav-wrap .chapter-area',
78405                         buttonText: _t('intro.ok'),
78406                         buttonCallback: function() { reveal('.ideditor'); }
78407                     }
78408                 );
78409             }
78410
78411
78412             chapter.enter = function() {
78413                 addPoint();
78414             };
78415
78416
78417             chapter.exit = function() {
78418                 timeouts.forEach(window.clearTimeout);
78419                 context.on('enter.intro exit.intro', null);
78420                 context.map().on('move.intro drawn.intro', null);
78421                 context.history().on('change.intro', null);
78422                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
78423                 context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
78424             };
78425
78426
78427             chapter.restart = function() {
78428                 chapter.exit();
78429                 chapter.enter();
78430             };
78431
78432
78433             return utilRebind(chapter, dispatch$1, 'on');
78434         }
78435
78436         function uiIntroArea(context, reveal) {
78437             var dispatch$1 = dispatch('done');
78438             var playground = [-85.63552, 41.94159];
78439             var playgroundPreset = _mainPresetIndex.item('leisure/playground');
78440             var nameField = _mainPresetIndex.field('name');
78441             var descriptionField = _mainPresetIndex.field('description');
78442             var timeouts = [];
78443             var _areaID;
78444
78445
78446             var chapter = {
78447                 title: 'intro.areas.title'
78448             };
78449
78450
78451             function timeout(f, t) {
78452                 timeouts.push(window.setTimeout(f, t));
78453             }
78454
78455
78456             function eventCancel() {
78457                 event.stopPropagation();
78458                 event.preventDefault();
78459             }
78460
78461
78462             function revealPlayground(center, text, options) {
78463                 var padding = 180 * Math.pow(2, context.map().zoom() - 19.5);
78464                 var box = pad(center, padding, context);
78465                 reveal(box, text, options);
78466             }
78467
78468
78469             function addArea() {
78470                 context.enter(modeBrowse(context));
78471                 context.history().reset('initial');
78472                 _areaID = null;
78473
78474                 var msec = transitionTime(playground, context.map().center());
78475                 if (msec) { reveal(null, null, { duration: 0 }); }
78476                 context.map().centerZoomEase(playground, 19, msec);
78477
78478                 timeout(function() {
78479                     var tooltip = reveal('button.add-area',
78480                         helpString('intro.areas.add_playground'));
78481
78482                     tooltip.selectAll('.popover-inner')
78483                         .insert('svg', 'span')
78484                         .attr('class', 'tooltip-illustration')
78485                         .append('use')
78486                         .attr('xlink:href', '#iD-graphic-areas');
78487
78488                     context.on('enter.intro', function(mode) {
78489                         if (mode.id !== 'add-area') { return; }
78490                         continueTo(startPlayground);
78491                     });
78492                 }, msec + 100);
78493
78494                 function continueTo(nextStep) {
78495                     context.on('enter.intro', null);
78496                     nextStep();
78497                 }
78498             }
78499
78500
78501             function startPlayground() {
78502                 if (context.mode().id !== 'add-area') {
78503                     return chapter.restart();
78504                 }
78505
78506                 _areaID = null;
78507                 context.map().zoomEase(19.5, 500);
78508
78509                 timeout(function() {
78510                     var textId = context.lastPointerType() === 'mouse' ? 'starting_node_click' : 'starting_node_tap';
78511                     var startDrawString = helpString('intro.areas.start_playground') + helpString('intro.areas.' + textId);
78512                     revealPlayground(playground,
78513                         startDrawString, { duration: 250 }
78514                     );
78515
78516                     timeout(function() {
78517                         context.map().on('move.intro drawn.intro', function() {
78518                             revealPlayground(playground,
78519                                 startDrawString, { duration: 0 }
78520                             );
78521                         });
78522                         context.on('enter.intro', function(mode) {
78523                             if (mode.id !== 'draw-area') { return chapter.restart(); }
78524                             continueTo(continuePlayground);
78525                         });
78526                     }, 250);  // after reveal
78527
78528                 }, 550);  // after easing
78529
78530                 function continueTo(nextStep) {
78531                     context.map().on('move.intro drawn.intro', null);
78532                     context.on('enter.intro', null);
78533                     nextStep();
78534                 }
78535             }
78536
78537
78538             function continuePlayground() {
78539                 if (context.mode().id !== 'draw-area') {
78540                     return chapter.restart();
78541                 }
78542
78543                 _areaID = null;
78544                 revealPlayground(playground,
78545                     helpString('intro.areas.continue_playground'),
78546                     { duration: 250 }
78547                 );
78548
78549                 timeout(function() {
78550                     context.map().on('move.intro drawn.intro', function() {
78551                         revealPlayground(playground,
78552                             helpString('intro.areas.continue_playground'),
78553                             { duration: 0 }
78554                         );
78555                     });
78556                 }, 250);  // after reveal
78557
78558                 context.on('enter.intro', function(mode) {
78559                     if (mode.id === 'draw-area') {
78560                         var entity = context.hasEntity(context.selectedIDs()[0]);
78561                         if (entity && entity.nodes.length >= 6) {
78562                             return continueTo(finishPlayground);
78563                         } else {
78564                             return;
78565                         }
78566                     } else if (mode.id === 'select') {
78567                         _areaID = context.selectedIDs()[0];
78568                         return continueTo(searchPresets);
78569                     } else {
78570                         return chapter.restart();
78571                     }
78572                 });
78573
78574                 function continueTo(nextStep) {
78575                     context.map().on('move.intro drawn.intro', null);
78576                     context.on('enter.intro', null);
78577                     nextStep();
78578                 }
78579             }
78580
78581
78582             function finishPlayground() {
78583                 if (context.mode().id !== 'draw-area') {
78584                     return chapter.restart();
78585                 }
78586
78587                 _areaID = null;
78588
78589                 var finishString = helpString('intro.areas.finish_area_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap')) +
78590                     helpString('intro.areas.finish_playground');
78591                 revealPlayground(playground,
78592                     finishString, { duration: 250 }
78593                 );
78594
78595                 timeout(function() {
78596                     context.map().on('move.intro drawn.intro', function() {
78597                         revealPlayground(playground,
78598                             finishString, { duration: 0 }
78599                         );
78600                     });
78601                 }, 250);  // after reveal
78602
78603                 context.on('enter.intro', function(mode) {
78604                     if (mode.id === 'draw-area') {
78605                         return;
78606                     } else if (mode.id === 'select') {
78607                         _areaID = context.selectedIDs()[0];
78608                         return continueTo(searchPresets);
78609                     } else {
78610                         return chapter.restart();
78611                     }
78612                 });
78613
78614                 function continueTo(nextStep) {
78615                     context.map().on('move.intro drawn.intro', null);
78616                     context.on('enter.intro', null);
78617                     nextStep();
78618                 }
78619             }
78620
78621
78622             function searchPresets() {
78623                 if (!_areaID || !context.hasEntity(_areaID)) {
78624                     return addArea();
78625                 }
78626                 var ids = context.selectedIDs();
78627                 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) {
78628                     context.enter(modeSelect(context, [_areaID]));
78629                 }
78630
78631                 // disallow scrolling
78632                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
78633
78634                 timeout(function() {
78635                     // reset pane, in case user somehow happened to change it..
78636                     context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
78637
78638                     context.container().select('.preset-search-input')
78639                         .on('keydown.intro', null)
78640                         .on('keyup.intro', checkPresetSearch);
78641
78642                     reveal('.preset-search-input',
78643                         helpString('intro.areas.search_playground', { preset: playgroundPreset.name() })
78644                     );
78645                 }, 400);  // after preset list pane visible..
78646
78647                 context.on('enter.intro', function(mode) {
78648                     if (!_areaID || !context.hasEntity(_areaID)) {
78649                         return continueTo(addArea);
78650                     }
78651
78652                     var ids = context.selectedIDs();
78653                     if (mode.id !== 'select' || !ids.length || ids[0] !== _areaID) {
78654                         // keep the user's area selected..
78655                         context.enter(modeSelect(context, [_areaID]));
78656
78657                         // reset pane, in case user somehow happened to change it..
78658                         context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
78659                         // disallow scrolling
78660                         context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
78661
78662                         context.container().select('.preset-search-input')
78663                             .on('keydown.intro', null)
78664                             .on('keyup.intro', checkPresetSearch);
78665
78666                         reveal('.preset-search-input',
78667                             helpString('intro.areas.search_playground', { preset: playgroundPreset.name() })
78668                         );
78669
78670                         context.history().on('change.intro', null);
78671                     }
78672                 });
78673
78674                 function checkPresetSearch() {
78675                     var first = context.container().select('.preset-list-item:first-child');
78676
78677                     if (first.classed('preset-leisure-playground')) {
78678                         reveal(first.select('.preset-list-button').node(),
78679                             helpString('intro.areas.choose_playground', { preset: playgroundPreset.name() }),
78680                             { duration: 300 }
78681                         );
78682
78683                         context.container().select('.preset-search-input')
78684                             .on('keydown.intro', eventCancel, true)
78685                             .on('keyup.intro', null);
78686
78687                         context.history().on('change.intro', function() {
78688                             continueTo(clickAddField);
78689                         });
78690                     }
78691                 }
78692
78693                 function continueTo(nextStep) {
78694                     context.container().select('.inspector-wrap').on('wheel.intro', null);
78695                     context.on('enter.intro', null);
78696                     context.history().on('change.intro', null);
78697                     context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
78698                     nextStep();
78699                 }
78700             }
78701
78702
78703             function clickAddField() {
78704                 if (!_areaID || !context.hasEntity(_areaID)) {
78705                     return addArea();
78706                 }
78707                 var ids = context.selectedIDs();
78708                 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) {
78709                     return searchPresets();
78710                 }
78711
78712                 if (!context.container().select('.form-field-description').empty()) {
78713                     return continueTo(describePlayground);
78714                 }
78715
78716                 // disallow scrolling
78717                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
78718
78719                 timeout(function() {
78720                     // reset pane, in case user somehow happened to change it..
78721                     context.container().select('.inspector-wrap .panewrap').style('right', '0%');
78722
78723                     // It's possible for the user to add a description in a previous step..
78724                     // If they did this already, just continue to next step.
78725                     var entity = context.entity(_areaID);
78726                     if (entity.tags.description) {
78727                         return continueTo(play);
78728                     }
78729
78730                     // scroll "Add field" into view
78731                     var box = context.container().select('.more-fields').node().getBoundingClientRect();
78732                     if (box.top > 300) {
78733                         var pane = context.container().select('.entity-editor-pane .inspector-body');
78734                         var start = pane.node().scrollTop;
78735                         var end = start + (box.top - 300);
78736
78737                         pane
78738                             .transition()
78739                             .duration(250)
78740                             .tween('scroll.inspector', function() {
78741                                 var node = this;
78742                                 var i = d3_interpolateNumber(start, end);
78743                                 return function(t) {
78744                                     node.scrollTop = i(t);
78745                                 };
78746                             });
78747                     }
78748
78749                     timeout(function() {
78750                         reveal('.more-fields .combobox-input',
78751                             helpString('intro.areas.add_field', {
78752                                 name: nameField.label(),
78753                                 description: descriptionField.label()
78754                             }),
78755                             { duration: 300 }
78756                         );
78757
78758                         context.container().select('.more-fields .combobox-input')
78759                             .on('click.intro', function() {
78760                                 // Watch for the combobox to appear...
78761                                 var watcher;
78762                                 watcher = window.setInterval(function() {
78763                                     if (!context.container().select('div.combobox').empty()) {
78764                                         window.clearInterval(watcher);
78765                                         continueTo(chooseDescriptionField);
78766                                     }
78767                                 }, 300);
78768                             });
78769                     }, 300);  // after "Add Field" visible
78770
78771                 }, 400);  // after editor pane visible
78772
78773                 context.on('exit.intro', function() {
78774                     return continueTo(searchPresets);
78775                 });
78776
78777                 function continueTo(nextStep) {
78778                     context.container().select('.inspector-wrap').on('wheel.intro', null);
78779                     context.container().select('.more-fields .combobox-input').on('click.intro', null);
78780                     context.on('exit.intro', null);
78781                     nextStep();
78782                 }
78783             }
78784
78785
78786             function chooseDescriptionField() {
78787                 if (!_areaID || !context.hasEntity(_areaID)) {
78788                     return addArea();
78789                 }
78790                 var ids = context.selectedIDs();
78791                 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) {
78792                     return searchPresets();
78793                 }
78794
78795                 if (!context.container().select('.form-field-description').empty()) {
78796                     return continueTo(describePlayground);
78797                 }
78798
78799                 // Make sure combobox is ready..
78800                 if (context.container().select('div.combobox').empty()) {
78801                     return continueTo(clickAddField);
78802                 }
78803                 // Watch for the combobox to go away..
78804                 var watcher;
78805                 watcher = window.setInterval(function() {
78806                     if (context.container().select('div.combobox').empty()) {
78807                         window.clearInterval(watcher);
78808                         timeout(function() {
78809                             if (context.container().select('.form-field-description').empty()) {
78810                                 continueTo(retryChooseDescription);
78811                             } else {
78812                                 continueTo(describePlayground);
78813                             }
78814                         }, 300);  // after description field added.
78815                     }
78816                 }, 300);
78817
78818                 reveal('div.combobox',
78819                     helpString('intro.areas.choose_field', { field: descriptionField.label() }),
78820                     { duration: 300 }
78821                 );
78822
78823                 context.on('exit.intro', function() {
78824                     return continueTo(searchPresets);
78825                 });
78826
78827                 function continueTo(nextStep) {
78828                     if (watcher) { window.clearInterval(watcher); }
78829                     context.on('exit.intro', null);
78830                     nextStep();
78831                 }
78832             }
78833
78834
78835             function describePlayground() {
78836                 if (!_areaID || !context.hasEntity(_areaID)) {
78837                     return addArea();
78838                 }
78839                 var ids = context.selectedIDs();
78840                 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) {
78841                     return searchPresets();
78842                 }
78843
78844                 // reset pane, in case user happened to change it..
78845                 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
78846
78847                 if (context.container().select('.form-field-description').empty()) {
78848                     return continueTo(retryChooseDescription);
78849                 }
78850
78851                 context.on('exit.intro', function() {
78852                     continueTo(play);
78853                 });
78854
78855                 reveal('.entity-editor-pane',
78856                     helpString('intro.areas.describe_playground', { button: icon('#iD-icon-close', 'pre-text') }),
78857                     { duration: 300 }
78858                 );
78859
78860                 function continueTo(nextStep) {
78861                     context.on('exit.intro', null);
78862                     nextStep();
78863                 }
78864             }
78865
78866
78867             function retryChooseDescription() {
78868                 if (!_areaID || !context.hasEntity(_areaID)) {
78869                     return addArea();
78870                 }
78871                 var ids = context.selectedIDs();
78872                 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) {
78873                     return searchPresets();
78874                 }
78875
78876                 // reset pane, in case user happened to change it..
78877                 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
78878
78879                 reveal('.entity-editor-pane',
78880                     helpString('intro.areas.retry_add_field', { field: descriptionField.label() }), {
78881                     buttonText: _t('intro.ok'),
78882                     buttonCallback: function() { continueTo(clickAddField); }
78883                 });
78884
78885                 context.on('exit.intro', function() {
78886                     return continueTo(searchPresets);
78887                 });
78888
78889                 function continueTo(nextStep) {
78890                     context.on('exit.intro', null);
78891                     nextStep();
78892                 }
78893             }
78894
78895
78896             function play() {
78897                 dispatch$1.call('done');
78898                 reveal('.ideditor',
78899                     helpString('intro.areas.play', { next: _t('intro.lines.title') }), {
78900                         tooltipBox: '.intro-nav-wrap .chapter-line',
78901                         buttonText: _t('intro.ok'),
78902                         buttonCallback: function() { reveal('.ideditor'); }
78903                     }
78904                 );
78905             }
78906
78907
78908             chapter.enter = function() {
78909                 addArea();
78910             };
78911
78912
78913             chapter.exit = function() {
78914                 timeouts.forEach(window.clearTimeout);
78915                 context.on('enter.intro exit.intro', null);
78916                 context.map().on('move.intro drawn.intro', null);
78917                 context.history().on('change.intro', null);
78918                 context.container().select('.inspector-wrap').on('wheel.intro', null);
78919                 context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
78920                 context.container().select('.more-fields .combobox-input').on('click.intro', null);
78921             };
78922
78923
78924             chapter.restart = function() {
78925                 chapter.exit();
78926                 chapter.enter();
78927             };
78928
78929
78930             return utilRebind(chapter, dispatch$1, 'on');
78931         }
78932
78933         function uiIntroLine(context, reveal) {
78934             var dispatch$1 = dispatch('done');
78935             var timeouts = [];
78936             var _tulipRoadID = null;
78937             var flowerRoadID = 'w646';
78938             var tulipRoadStart = [-85.6297754121684, 41.95805253325314];
78939             var tulipRoadMidpoint = [-85.62975395449628, 41.95787501510204];
78940             var tulipRoadIntersection = [-85.62974496187628, 41.95742515554585];
78941             var roadCategory = _mainPresetIndex.item('category-road_minor');
78942             var residentialPreset = _mainPresetIndex.item('highway/residential');
78943             var woodRoadID = 'w525';
78944             var woodRoadEndID = 'n2862';
78945             var woodRoadAddNode = [-85.62390110349587, 41.95397111462291];
78946             var woodRoadDragEndpoint = [-85.623867390213, 41.95466987786487];
78947             var woodRoadDragMidpoint = [-85.62386254803509, 41.95430395953872];
78948             var washingtonStreetID = 'w522';
78949             var twelfthAvenueID = 'w1';
78950             var eleventhAvenueEndID = 'n3550';
78951             var twelfthAvenueEndID = 'n5';
78952             var _washingtonSegmentID = null;
78953             var eleventhAvenueEnd = context.entity(eleventhAvenueEndID).loc;
78954             var twelfthAvenueEnd = context.entity(twelfthAvenueEndID).loc;
78955             var deleteLinesLoc = [-85.6219395542764, 41.95228033922477];
78956             var twelfthAvenue = [-85.62219310052491, 41.952505413152956];
78957
78958
78959             var chapter = {
78960                 title: 'intro.lines.title'
78961             };
78962
78963
78964             function timeout(f, t) {
78965                 timeouts.push(window.setTimeout(f, t));
78966             }
78967
78968
78969             function eventCancel() {
78970                 event.stopPropagation();
78971                 event.preventDefault();
78972             }
78973
78974
78975             function addLine() {
78976                 context.enter(modeBrowse(context));
78977                 context.history().reset('initial');
78978
78979                 var msec = transitionTime(tulipRoadStart, context.map().center());
78980                 if (msec) { reveal(null, null, { duration: 0 }); }
78981                 context.map().centerZoomEase(tulipRoadStart, 18.5, msec);
78982
78983                 timeout(function() {
78984                     var tooltip = reveal('button.add-line',
78985                         helpString('intro.lines.add_line'));
78986
78987                     tooltip.selectAll('.popover-inner')
78988                         .insert('svg', 'span')
78989                         .attr('class', 'tooltip-illustration')
78990                         .append('use')
78991                         .attr('xlink:href', '#iD-graphic-lines');
78992
78993                     context.on('enter.intro', function(mode) {
78994                         if (mode.id !== 'add-line') { return; }
78995                         continueTo(startLine);
78996                     });
78997                 }, msec + 100);
78998
78999                 function continueTo(nextStep) {
79000                     context.on('enter.intro', null);
79001                     nextStep();
79002                 }
79003             }
79004
79005
79006             function startLine() {
79007                 if (context.mode().id !== 'add-line') { return chapter.restart(); }
79008
79009                 _tulipRoadID = null;
79010
79011                 var padding = 70 * Math.pow(2, context.map().zoom() - 18);
79012                 var box = pad(tulipRoadStart, padding, context);
79013                 box.height = box.height + 100;
79014
79015                 var textId = context.lastPointerType() === 'mouse' ? 'start_line' : 'start_line_tap';
79016                 var startLineString = helpString('intro.lines.missing_road') + '{br}' +
79017                     helpString('intro.lines.line_draw_info') +
79018                     helpString('intro.lines.' + textId);
79019                 reveal(box, startLineString);
79020
79021                 context.map().on('move.intro drawn.intro', function() {
79022                     padding = 70 * Math.pow(2, context.map().zoom() - 18);
79023                     box = pad(tulipRoadStart, padding, context);
79024                     box.height = box.height + 100;
79025                     reveal(box, startLineString, { duration: 0 });
79026                 });
79027
79028                 context.on('enter.intro', function(mode) {
79029                     if (mode.id !== 'draw-line') { return chapter.restart(); }
79030                     continueTo(drawLine);
79031                 });
79032
79033                 function continueTo(nextStep) {
79034                     context.map().on('move.intro drawn.intro', null);
79035                     context.on('enter.intro', null);
79036                     nextStep();
79037                 }
79038             }
79039
79040
79041             function drawLine() {
79042                 if (context.mode().id !== 'draw-line') { return chapter.restart(); }
79043
79044                 _tulipRoadID = context.mode().selectedIDs()[0];
79045                 context.map().centerEase(tulipRoadMidpoint, 500);
79046
79047                 timeout(function() {
79048                     var padding = 200 * Math.pow(2, context.map().zoom() - 18.5);
79049                     var box = pad(tulipRoadMidpoint, padding, context);
79050                     box.height = box.height * 2;
79051                     reveal(box,
79052                         helpString('intro.lines.intersect', { name: _t('intro.graph.name.flower-street') })
79053                     );
79054
79055                     context.map().on('move.intro drawn.intro', function() {
79056                         padding = 200 * Math.pow(2, context.map().zoom() - 18.5);
79057                         box = pad(tulipRoadMidpoint, padding, context);
79058                         box.height = box.height * 2;
79059                         reveal(box,
79060                             helpString('intro.lines.intersect', { name: _t('intro.graph.name.flower-street') }),
79061                             { duration: 0 }
79062                         );
79063                     });
79064                 }, 550);  // after easing..
79065
79066                 context.history().on('change.intro', function() {
79067                     if (isLineConnected()) {
79068                         continueTo(continueLine);
79069                     }
79070                 });
79071
79072                 context.on('enter.intro', function(mode) {
79073                     if (mode.id === 'draw-line') {
79074                         return;
79075                     } else if (mode.id === 'select') {
79076                         continueTo(retryIntersect);
79077                         return;
79078                     } else {
79079                         return chapter.restart();
79080                     }
79081                 });
79082
79083                 function continueTo(nextStep) {
79084                     context.map().on('move.intro drawn.intro', null);
79085                     context.history().on('change.intro', null);
79086                     context.on('enter.intro', null);
79087                     nextStep();
79088                 }
79089             }
79090
79091
79092             function isLineConnected() {
79093                 var entity = _tulipRoadID && context.hasEntity(_tulipRoadID);
79094                 if (!entity) { return false; }
79095
79096                 var drawNodes = context.graph().childNodes(entity);
79097                 return drawNodes.some(function(node) {
79098                     return context.graph().parentWays(node).some(function(parent) {
79099                         return parent.id === flowerRoadID;
79100                     });
79101                 });
79102             }
79103
79104
79105             function retryIntersect() {
79106                 select(window).on('pointerdown.intro mousedown.intro', eventCancel, true);
79107
79108                 var box = pad(tulipRoadIntersection, 80, context);
79109                 reveal(box,
79110                     helpString('intro.lines.retry_intersect', { name: _t('intro.graph.name.flower-street') })
79111                 );
79112
79113                 timeout(chapter.restart, 3000);
79114             }
79115
79116
79117             function continueLine() {
79118                 if (context.mode().id !== 'draw-line') { return chapter.restart(); }
79119                 var entity = _tulipRoadID && context.hasEntity(_tulipRoadID);
79120                 if (!entity) { return chapter.restart(); }
79121
79122                 context.map().centerEase(tulipRoadIntersection, 500);
79123
79124                 var continueLineText = helpString('intro.lines.continue_line') + '{br}' +
79125                     helpString('intro.lines.finish_line_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap')) +
79126                     helpString('intro.lines.finish_road');
79127
79128                 reveal('.surface', continueLineText);
79129
79130                 context.on('enter.intro', function(mode) {
79131                     if (mode.id === 'draw-line')
79132                         { return; }
79133                     else if (mode.id === 'select')
79134                         { return continueTo(chooseCategoryRoad); }
79135                     else
79136                         { return chapter.restart(); }
79137                 });
79138
79139                 function continueTo(nextStep) {
79140                     context.on('enter.intro', null);
79141                     nextStep();
79142                 }
79143             }
79144
79145
79146             function chooseCategoryRoad() {
79147                 if (context.mode().id !== 'select') { return chapter.restart(); }
79148
79149                 context.on('exit.intro', function() {
79150                     return chapter.restart();
79151                 });
79152
79153                 var button = context.container().select('.preset-category-road_minor .preset-list-button');
79154                 if (button.empty()) { return chapter.restart(); }
79155
79156                 // disallow scrolling
79157                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
79158
79159                 timeout(function() {
79160                     // reset pane, in case user somehow happened to change it..
79161                     context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
79162
79163                     reveal(button.node(),
79164                         helpString('intro.lines.choose_category_road', { category: roadCategory.name() })
79165                     );
79166
79167                     button.on('click.intro', function() {
79168                         continueTo(choosePresetResidential);
79169                     });
79170
79171                 }, 400);  // after editor pane visible
79172
79173                 function continueTo(nextStep) {
79174                     context.container().select('.inspector-wrap').on('wheel.intro', null);
79175                     context.container().select('.preset-list-button').on('click.intro', null);
79176                     context.on('exit.intro', null);
79177                     nextStep();
79178                 }
79179             }
79180
79181
79182             function choosePresetResidential() {
79183                 if (context.mode().id !== 'select') { return chapter.restart(); }
79184
79185                 context.on('exit.intro', function() {
79186                     return chapter.restart();
79187                 });
79188
79189                 var subgrid = context.container().select('.preset-category-road_minor .subgrid');
79190                 if (subgrid.empty()) { return chapter.restart(); }
79191
79192                 subgrid.selectAll(':not(.preset-highway-residential) .preset-list-button')
79193                     .on('click.intro', function() {
79194                         continueTo(retryPresetResidential);
79195                     });
79196
79197                 subgrid.selectAll('.preset-highway-residential .preset-list-button')
79198                     .on('click.intro', function() {
79199                         continueTo(nameRoad);
79200                     });
79201
79202                 timeout(function() {
79203                     reveal(subgrid.node(),
79204                         helpString('intro.lines.choose_preset_residential', { preset: residentialPreset.name() }),
79205                         { tooltipBox: '.preset-highway-residential .preset-list-button', duration: 300 }
79206                     );
79207                 }, 300);
79208
79209                 function continueTo(nextStep) {
79210                     context.container().select('.preset-list-button').on('click.intro', null);
79211                     context.on('exit.intro', null);
79212                     nextStep();
79213                 }
79214             }
79215
79216
79217             // selected wrong road type
79218             function retryPresetResidential() {
79219                 if (context.mode().id !== 'select') { return chapter.restart(); }
79220
79221                 context.on('exit.intro', function() {
79222                     return chapter.restart();
79223                 });
79224
79225                 // disallow scrolling
79226                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
79227
79228                 timeout(function() {
79229                     var button = context.container().select('.entity-editor-pane .preset-list-button');
79230
79231                     reveal(button.node(),
79232                         helpString('intro.lines.retry_preset_residential', { preset: residentialPreset.name() })
79233                     );
79234
79235                     button.on('click.intro', function() {
79236                         continueTo(chooseCategoryRoad);
79237                     });
79238
79239                 }, 500);
79240
79241                 function continueTo(nextStep) {
79242                     context.container().select('.inspector-wrap').on('wheel.intro', null);
79243                     context.container().select('.preset-list-button').on('click.intro', null);
79244                     context.on('exit.intro', null);
79245                     nextStep();
79246                 }
79247             }
79248
79249
79250             function nameRoad() {
79251                 context.on('exit.intro', function() {
79252                     continueTo(didNameRoad);
79253                 });
79254
79255                 timeout(function() {
79256                     reveal('.entity-editor-pane',
79257                         helpString('intro.lines.name_road', { button: icon('#iD-icon-close', 'pre-text') }),
79258                         { tooltipClass: 'intro-lines-name_road' }
79259                     );
79260                 }, 500);
79261
79262                 function continueTo(nextStep) {
79263                     context.on('exit.intro', null);
79264                     nextStep();
79265                 }
79266             }
79267
79268
79269             function didNameRoad() {
79270                 context.history().checkpoint('doneAddLine');
79271
79272                 timeout(function() {
79273                     reveal('.surface', helpString('intro.lines.did_name_road'), {
79274                         buttonText: _t('intro.ok'),
79275                         buttonCallback: function() { continueTo(updateLine); }
79276                     });
79277                 }, 500);
79278
79279                 function continueTo(nextStep) {
79280                     nextStep();
79281                 }
79282             }
79283
79284
79285             function updateLine() {
79286                 context.history().reset('doneAddLine');
79287                 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
79288                     return chapter.restart();
79289                 }
79290
79291                 var msec = transitionTime(woodRoadDragMidpoint, context.map().center());
79292                 if (msec) { reveal(null, null, { duration: 0 }); }
79293                 context.map().centerZoomEase(woodRoadDragMidpoint, 19, msec);
79294
79295                 timeout(function() {
79296                     var padding = 250 * Math.pow(2, context.map().zoom() - 19);
79297                     var box = pad(woodRoadDragMidpoint, padding, context);
79298                     var advance = function() { continueTo(addNode); };
79299
79300                     reveal(box, helpString('intro.lines.update_line'),
79301                         { buttonText: _t('intro.ok'), buttonCallback: advance }
79302                     );
79303
79304                     context.map().on('move.intro drawn.intro', function() {
79305                         var padding = 250 * Math.pow(2, context.map().zoom() - 19);
79306                         var box = pad(woodRoadDragMidpoint, padding, context);
79307                         reveal(box, helpString('intro.lines.update_line'),
79308                             { duration: 0, buttonText: _t('intro.ok'), buttonCallback: advance }
79309                         );
79310                     });
79311                 }, msec + 100);
79312
79313                 function continueTo(nextStep) {
79314                     context.map().on('move.intro drawn.intro', null);
79315                     nextStep();
79316                 }
79317             }
79318
79319
79320             function addNode() {
79321                 context.history().reset('doneAddLine');
79322                 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
79323                     return chapter.restart();
79324                 }
79325
79326                 var padding = 40 * Math.pow(2, context.map().zoom() - 19);
79327                 var box = pad(woodRoadAddNode, padding, context);
79328                 var addNodeString = helpString('intro.lines.add_node' + (context.lastPointerType() === 'mouse' ? '' : '_touch'));
79329                 reveal(box, addNodeString);
79330
79331                 context.map().on('move.intro drawn.intro', function() {
79332                     var padding = 40 * Math.pow(2, context.map().zoom() - 19);
79333                     var box = pad(woodRoadAddNode, padding, context);
79334                     reveal(box, addNodeString, { duration: 0 });
79335                 });
79336
79337                 context.history().on('change.intro', function(changed) {
79338                     if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
79339                         return continueTo(updateLine);
79340                     }
79341                     if (changed.created().length === 1) {
79342                         timeout(function() { continueTo(startDragEndpoint); }, 500);
79343                     }
79344                 });
79345
79346                 context.on('enter.intro', function(mode) {
79347                     if (mode.id !== 'select') {
79348                         continueTo(updateLine);
79349                     }
79350                 });
79351
79352                 function continueTo(nextStep) {
79353                     context.map().on('move.intro drawn.intro', null);
79354                     context.history().on('change.intro', null);
79355                     context.on('enter.intro', null);
79356                     nextStep();
79357                 }
79358             }
79359
79360
79361             function startDragEndpoint() {
79362                 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
79363                     return continueTo(updateLine);
79364                 }
79365                 var padding = 100 * Math.pow(2, context.map().zoom() - 19);
79366                 var box = pad(woodRoadDragEndpoint, padding, context);
79367                 var startDragString = helpString('intro.lines.start_drag_endpoint' + (context.lastPointerType() === 'mouse' ? '' : '_touch')) +
79368                     helpString('intro.lines.drag_to_intersection');
79369                 reveal(box, startDragString);
79370
79371                 context.map().on('move.intro drawn.intro', function() {
79372                     if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
79373                         return continueTo(updateLine);
79374                     }
79375                     var padding = 100 * Math.pow(2, context.map().zoom() - 19);
79376                     var box = pad(woodRoadDragEndpoint, padding, context);
79377                     reveal(box, startDragString, { duration: 0 });
79378
79379                     var entity = context.entity(woodRoadEndID);
79380                     if (geoSphericalDistance(entity.loc, woodRoadDragEndpoint) <= 4) {
79381                         continueTo(finishDragEndpoint);
79382                     }
79383                 });
79384
79385                 function continueTo(nextStep) {
79386                     context.map().on('move.intro drawn.intro', null);
79387                     nextStep();
79388                 }
79389             }
79390
79391
79392             function finishDragEndpoint() {
79393                 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
79394                     return continueTo(updateLine);
79395                 }
79396
79397                 var padding = 100 * Math.pow(2, context.map().zoom() - 19);
79398                 var box = pad(woodRoadDragEndpoint, padding, context);
79399                 var finishDragString = helpString('intro.lines.spot_looks_good') +
79400                     helpString('intro.lines.finish_drag_endpoint' + (context.lastPointerType() === 'mouse' ? '' : '_touch'));
79401                 reveal(box, finishDragString);
79402
79403                 context.map().on('move.intro drawn.intro', function() {
79404                     if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
79405                         return continueTo(updateLine);
79406                     }
79407                     var padding = 100 * Math.pow(2, context.map().zoom() - 19);
79408                     var box = pad(woodRoadDragEndpoint, padding, context);
79409                     reveal(box, finishDragString, { duration: 0 });
79410
79411                     var entity = context.entity(woodRoadEndID);
79412                     if (geoSphericalDistance(entity.loc, woodRoadDragEndpoint) > 4) {
79413                         continueTo(startDragEndpoint);
79414                     }
79415                 });
79416
79417                 context.on('enter.intro', function() {
79418                     continueTo(startDragMidpoint);
79419                 });
79420
79421                 function continueTo(nextStep) {
79422                     context.map().on('move.intro drawn.intro', null);
79423                     context.on('enter.intro', null);
79424                     nextStep();
79425                 }
79426             }
79427
79428
79429             function startDragMidpoint() {
79430                 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
79431                     return continueTo(updateLine);
79432                 }
79433                 if (context.selectedIDs().indexOf(woodRoadID) === -1) {
79434                     context.enter(modeSelect(context, [woodRoadID]));
79435                 }
79436
79437                 var padding = 80 * Math.pow(2, context.map().zoom() - 19);
79438                 var box = pad(woodRoadDragMidpoint, padding, context);
79439                 reveal(box, helpString('intro.lines.start_drag_midpoint'));
79440
79441                 context.map().on('move.intro drawn.intro', function() {
79442                     if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
79443                         return continueTo(updateLine);
79444                     }
79445                     var padding = 80 * Math.pow(2, context.map().zoom() - 19);
79446                     var box = pad(woodRoadDragMidpoint, padding, context);
79447                     reveal(box, helpString('intro.lines.start_drag_midpoint'), { duration: 0 });
79448                 });
79449
79450                 context.history().on('change.intro', function(changed) {
79451                     if (changed.created().length === 1) {
79452                         continueTo(continueDragMidpoint);
79453                     }
79454                 });
79455
79456                 context.on('enter.intro', function(mode) {
79457                     if (mode.id !== 'select') {
79458                         // keep Wood Road selected so midpoint triangles are drawn..
79459                         context.enter(modeSelect(context, [woodRoadID]));
79460                     }
79461                 });
79462
79463                 function continueTo(nextStep) {
79464                     context.map().on('move.intro drawn.intro', null);
79465                     context.history().on('change.intro', null);
79466                     context.on('enter.intro', null);
79467                     nextStep();
79468                 }
79469             }
79470
79471
79472             function continueDragMidpoint() {
79473                 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
79474                     return continueTo(updateLine);
79475                 }
79476
79477                 var padding = 100 * Math.pow(2, context.map().zoom() - 19);
79478                 var box = pad(woodRoadDragEndpoint, padding, context);
79479                 box.height += 400;
79480
79481                 var advance = function() {
79482                     context.history().checkpoint('doneUpdateLine');
79483                     continueTo(deleteLines);
79484                 };
79485
79486                 reveal(box, helpString('intro.lines.continue_drag_midpoint'),
79487                     { buttonText: _t('intro.ok'), buttonCallback: advance }
79488                 );
79489
79490                 context.map().on('move.intro drawn.intro', function() {
79491                     if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
79492                         return continueTo(updateLine);
79493                     }
79494                     var padding = 100 * Math.pow(2, context.map().zoom() - 19);
79495                     var box = pad(woodRoadDragEndpoint, padding, context);
79496                     box.height += 400;
79497                     reveal(box, helpString('intro.lines.continue_drag_midpoint'),
79498                         { duration: 0, buttonText: _t('intro.ok'), buttonCallback: advance }
79499                     );
79500                 });
79501
79502                 function continueTo(nextStep) {
79503                     context.map().on('move.intro drawn.intro', null);
79504                     nextStep();
79505                 }
79506             }
79507
79508
79509             function deleteLines() {
79510                 context.history().reset('doneUpdateLine');
79511                 context.enter(modeBrowse(context));
79512
79513                 if (!context.hasEntity(washingtonStreetID) ||
79514                     !context.hasEntity(twelfthAvenueID) ||
79515                     !context.hasEntity(eleventhAvenueEndID)) {
79516                     return chapter.restart();
79517                 }
79518
79519                 var msec = transitionTime(deleteLinesLoc, context.map().center());
79520                 if (msec) { reveal(null, null, { duration: 0 }); }
79521                 context.map().centerZoomEase(deleteLinesLoc, 18, msec);
79522
79523                 timeout(function() {
79524                     var padding = 200 * Math.pow(2, context.map().zoom() - 18);
79525                     var box = pad(deleteLinesLoc, padding, context);
79526                     box.top -= 200;
79527                     box.height += 400;
79528                     var advance = function() { continueTo(rightClickIntersection); };
79529
79530                     reveal(box, helpString('intro.lines.delete_lines', { street: _t('intro.graph.name.12th-avenue') }),
79531                         { buttonText: _t('intro.ok'), buttonCallback: advance }
79532                     );
79533
79534                     context.map().on('move.intro drawn.intro', function() {
79535                         var padding = 200 * Math.pow(2, context.map().zoom() - 18);
79536                         var box = pad(deleteLinesLoc, padding, context);
79537                         box.top -= 200;
79538                         box.height += 400;
79539                         reveal(box, helpString('intro.lines.delete_lines', { street: _t('intro.graph.name.12th-avenue') }),
79540                             { duration: 0, buttonText: _t('intro.ok'), buttonCallback: advance }
79541                         );
79542                     });
79543
79544                     context.history().on('change.intro', function() {
79545                         timeout(function() {
79546                             continueTo(deleteLines);
79547                         }, 500);  // after any transition (e.g. if user deleted intersection)
79548                     });
79549
79550                 }, msec + 100);
79551
79552                 function continueTo(nextStep) {
79553                     context.map().on('move.intro drawn.intro', null);
79554                     context.history().on('change.intro', null);
79555                     nextStep();
79556                 }
79557             }
79558
79559
79560             function rightClickIntersection() {
79561                 context.history().reset('doneUpdateLine');
79562                 context.enter(modeBrowse(context));
79563
79564                 context.map().centerZoomEase(eleventhAvenueEnd, 18, 500);
79565
79566                 var rightClickString = helpString('intro.lines.split_street', {
79567                         street1: _t('intro.graph.name.11th-avenue'),
79568                         street2: _t('intro.graph.name.washington-street')
79569                     }) +
79570                     helpString('intro.lines.' + (context.lastPointerType() === 'mouse' ? 'rightclick_intersection' : 'edit_menu_intersection_touch'));
79571
79572                 timeout(function() {
79573                     var padding = 60 * Math.pow(2, context.map().zoom() - 18);
79574                     var box = pad(eleventhAvenueEnd, padding, context);
79575                     reveal(box, rightClickString);
79576
79577                     context.map().on('move.intro drawn.intro', function() {
79578                         var padding = 60 * Math.pow(2, context.map().zoom() - 18);
79579                         var box = pad(eleventhAvenueEnd, padding, context);
79580                         reveal(box, rightClickString,
79581                             { duration: 0 }
79582                         );
79583                     });
79584
79585                     context.on('enter.intro', function(mode) {
79586                         if (mode.id !== 'select') { return; }
79587                         var ids = context.selectedIDs();
79588                         if (ids.length !== 1 || ids[0] !== eleventhAvenueEndID) { return; }
79589
79590                         timeout(function() {
79591                             var node = selectMenuItem(context, 'split').node();
79592                             if (!node) { return; }
79593                             continueTo(splitIntersection);
79594                         }, 50);  // after menu visible
79595                     });
79596
79597                     context.history().on('change.intro', function() {
79598                         timeout(function() {
79599                             continueTo(deleteLines);
79600                         }, 300);  // after any transition (e.g. if user deleted intersection)
79601                     });
79602
79603                 }, 600);
79604
79605                 function continueTo(nextStep) {
79606                     context.map().on('move.intro drawn.intro', null);
79607                     context.on('enter.intro', null);
79608                     context.history().on('change.intro', null);
79609                     nextStep();
79610                 }
79611             }
79612
79613
79614             function splitIntersection() {
79615                 if (!context.hasEntity(washingtonStreetID) ||
79616                     !context.hasEntity(twelfthAvenueID) ||
79617                     !context.hasEntity(eleventhAvenueEndID)) {
79618                     return continueTo(deleteLines);
79619                 }
79620
79621                 var node = selectMenuItem(context, 'split').node();
79622                 if (!node) { return continueTo(rightClickIntersection); }
79623
79624                 var wasChanged = false;
79625                 _washingtonSegmentID = null;
79626
79627                 reveal('.edit-menu', helpString('intro.lines.split_intersection',
79628                     { street: _t('intro.graph.name.washington-street') }),
79629                     { padding: 50 }
79630                 );
79631
79632                 context.map().on('move.intro drawn.intro', function() {
79633                     var node = selectMenuItem(context, 'split').node();
79634                     if (!wasChanged && !node) { return continueTo(rightClickIntersection); }
79635
79636                     reveal('.edit-menu', helpString('intro.lines.split_intersection',
79637                         { street: _t('intro.graph.name.washington-street') }),
79638                         { duration: 0, padding: 50 }
79639                     );
79640                 });
79641
79642                 context.history().on('change.intro', function(changed) {
79643                     wasChanged = true;
79644                     timeout(function() {
79645                         if (context.history().undoAnnotation() === _t('operations.split.annotation.line')) {
79646                             _washingtonSegmentID = changed.created()[0].id;
79647                             continueTo(didSplit);
79648                         } else {
79649                             _washingtonSegmentID = null;
79650                             continueTo(retrySplit);
79651                         }
79652                     }, 300);  // after any transition (e.g. if user deleted intersection)
79653                 });
79654
79655                 function continueTo(nextStep) {
79656                     context.map().on('move.intro drawn.intro', null);
79657                     context.history().on('change.intro', null);
79658                     nextStep();
79659                 }
79660             }
79661
79662
79663             function retrySplit() {
79664                 context.enter(modeBrowse(context));
79665                 context.map().centerZoomEase(eleventhAvenueEnd, 18, 500);
79666                 var advance = function() { continueTo(rightClickIntersection); };
79667
79668                 var padding = 60 * Math.pow(2, context.map().zoom() - 18);
79669                 var box = pad(eleventhAvenueEnd, padding, context);
79670                 reveal(box, helpString('intro.lines.retry_split'),
79671                     { buttonText: _t('intro.ok'), buttonCallback: advance }
79672                 );
79673
79674                 context.map().on('move.intro drawn.intro', function() {
79675                     var padding = 60 * Math.pow(2, context.map().zoom() - 18);
79676                     var box = pad(eleventhAvenueEnd, padding, context);
79677                     reveal(box, helpString('intro.lines.retry_split'),
79678                         { duration: 0, buttonText: _t('intro.ok'), buttonCallback: advance }
79679                     );
79680                 });
79681
79682                 function continueTo(nextStep) {
79683                     context.map().on('move.intro drawn.intro', null);
79684                     nextStep();
79685                 }
79686             }
79687
79688
79689             function didSplit() {
79690                 if (!_washingtonSegmentID ||
79691                     !context.hasEntity(_washingtonSegmentID) ||
79692                     !context.hasEntity(washingtonStreetID) ||
79693                     !context.hasEntity(twelfthAvenueID) ||
79694                     !context.hasEntity(eleventhAvenueEndID)) {
79695                     return continueTo(rightClickIntersection);
79696                 }
79697
79698                 var ids = context.selectedIDs();
79699                 var string = 'intro.lines.did_split_' + (ids.length > 1 ? 'multi' : 'single');
79700                 var street = _t('intro.graph.name.washington-street');
79701
79702                 var padding = 200 * Math.pow(2, context.map().zoom() - 18);
79703                 var box = pad(twelfthAvenue, padding, context);
79704                 box.width = box.width / 2;
79705                 reveal(box, helpString(string, { street1: street, street2: street }),
79706                     { duration: 500 }
79707                 );
79708
79709                 timeout(function() {
79710                     context.map().centerZoomEase(twelfthAvenue, 18, 500);
79711
79712                     context.map().on('move.intro drawn.intro', function() {
79713                         var padding = 200 * Math.pow(2, context.map().zoom() - 18);
79714                         var box = pad(twelfthAvenue, padding, context);
79715                         box.width = box.width / 2;
79716                         reveal(box, helpString(string, { street1: street, street2: street }),
79717                             { duration: 0 }
79718                         );
79719                     });
79720                 }, 600);  // after initial reveal and curtain cut
79721
79722                 context.on('enter.intro', function() {
79723                     var ids = context.selectedIDs();
79724                     if (ids.length === 1 && ids[0] === _washingtonSegmentID) {
79725                         continueTo(multiSelect);
79726                     }
79727                 });
79728
79729                 context.history().on('change.intro', function() {
79730                     if (!_washingtonSegmentID ||
79731                         !context.hasEntity(_washingtonSegmentID) ||
79732                         !context.hasEntity(washingtonStreetID) ||
79733                         !context.hasEntity(twelfthAvenueID) ||
79734                         !context.hasEntity(eleventhAvenueEndID)) {
79735                         return continueTo(rightClickIntersection);
79736                     }
79737                 });
79738
79739                 function continueTo(nextStep) {
79740                     context.map().on('move.intro drawn.intro', null);
79741                     context.on('enter.intro', null);
79742                     context.history().on('change.intro', null);
79743                     nextStep();
79744                 }
79745             }
79746
79747
79748             function multiSelect() {
79749                 if (!_washingtonSegmentID ||
79750                     !context.hasEntity(_washingtonSegmentID) ||
79751                     !context.hasEntity(washingtonStreetID) ||
79752                     !context.hasEntity(twelfthAvenueID) ||
79753                     !context.hasEntity(eleventhAvenueEndID)) {
79754                     return continueTo(rightClickIntersection);
79755                 }
79756
79757                 var ids = context.selectedIDs();
79758                 var hasWashington = ids.indexOf(_washingtonSegmentID) !== -1;
79759                 var hasTwelfth = ids.indexOf(twelfthAvenueID) !== -1;
79760
79761                 if (hasWashington && hasTwelfth) {
79762                     return continueTo(multiRightClick);
79763                 } else if (!hasWashington && !hasTwelfth) {
79764                     return continueTo(didSplit);
79765                 }
79766
79767                 context.map().centerZoomEase(twelfthAvenue, 18, 500);
79768
79769                 timeout(function() {
79770                     var selected, other, padding, box;
79771                     if (hasWashington) {
79772                         selected = _t('intro.graph.name.washington-street');
79773                         other = _t('intro.graph.name.12th-avenue');
79774                         padding = 60 * Math.pow(2, context.map().zoom() - 18);
79775                         box = pad(twelfthAvenueEnd, padding, context);
79776                         box.width *= 3;
79777                     } else {
79778                         selected = _t('intro.graph.name.12th-avenue');
79779                         other = _t('intro.graph.name.washington-street');
79780                         padding = 200 * Math.pow(2, context.map().zoom() - 18);
79781                         box = pad(twelfthAvenue, padding, context);
79782                         box.width /= 2;
79783                     }
79784
79785                     reveal(box,
79786                         helpString('intro.lines.multi_select',
79787                             { selected: selected, other1: other }) + ' ' +
79788                         helpString('intro.lines.add_to_selection_' + (context.lastPointerType() === 'mouse' ? 'click' : 'touch'),
79789                             { selected: selected, other2: other })
79790                     );
79791
79792                     context.map().on('move.intro drawn.intro', function() {
79793                         if (hasWashington) {
79794                             selected = _t('intro.graph.name.washington-street');
79795                             other = _t('intro.graph.name.12th-avenue');
79796                             padding = 60 * Math.pow(2, context.map().zoom() - 18);
79797                             box = pad(twelfthAvenueEnd, padding, context);
79798                             box.width *= 3;
79799                         } else {
79800                             selected = _t('intro.graph.name.12th-avenue');
79801                             other = _t('intro.graph.name.washington-street');
79802                             padding = 200 * Math.pow(2, context.map().zoom() - 18);
79803                             box = pad(twelfthAvenue, padding, context);
79804                             box.width /= 2;
79805                         }
79806
79807                         reveal(box,
79808                             helpString('intro.lines.multi_select',
79809                                 { selected: selected, other1: other }) + ' ' +
79810                             helpString('intro.lines.add_to_selection_' + (context.lastPointerType() === 'mouse' ? 'click' : 'touch'),
79811                                 { selected: selected, other2: other }),
79812                             { duration: 0 }
79813                         );
79814                     });
79815
79816                     context.on('enter.intro', function() {
79817                         continueTo(multiSelect);
79818                     });
79819
79820                     context.history().on('change.intro', function() {
79821                         if (!_washingtonSegmentID ||
79822                             !context.hasEntity(_washingtonSegmentID) ||
79823                             !context.hasEntity(washingtonStreetID) ||
79824                             !context.hasEntity(twelfthAvenueID) ||
79825                             !context.hasEntity(eleventhAvenueEndID)) {
79826                             return continueTo(rightClickIntersection);
79827                         }
79828                     });
79829                 }, 600);
79830
79831                 function continueTo(nextStep) {
79832                     context.map().on('move.intro drawn.intro', null);
79833                     context.on('enter.intro', null);
79834                     context.history().on('change.intro', null);
79835                     nextStep();
79836                 }
79837             }
79838
79839
79840             function multiRightClick() {
79841                 if (!_washingtonSegmentID ||
79842                     !context.hasEntity(_washingtonSegmentID) ||
79843                     !context.hasEntity(washingtonStreetID) ||
79844                     !context.hasEntity(twelfthAvenueID) ||
79845                     !context.hasEntity(eleventhAvenueEndID)) {
79846                     return continueTo(rightClickIntersection);
79847                 }
79848
79849                 var padding = 200 * Math.pow(2, context.map().zoom() - 18);
79850                 var box = pad(twelfthAvenue, padding, context);
79851
79852                 var rightClickString = helpString('intro.lines.multi_select_success') +
79853                     helpString('intro.lines.multi_' + (context.lastPointerType() === 'mouse' ? 'rightclick' : 'edit_menu_touch'));
79854                 reveal(box, rightClickString);
79855
79856                 context.map().on('move.intro drawn.intro', function() {
79857                     var padding = 200 * Math.pow(2, context.map().zoom() - 18);
79858                     var box = pad(twelfthAvenue, padding, context);
79859                     reveal(box, rightClickString, { duration: 0 });
79860                 });
79861
79862                 context.ui().editMenu().on('toggled.intro', function(open) {
79863                     if (!open) { return; }
79864
79865                     timeout(function() {
79866                         var ids = context.selectedIDs();
79867                         if (ids.length === 2 &&
79868                             ids.indexOf(twelfthAvenueID) !== -1 &&
79869                             ids.indexOf(_washingtonSegmentID) !== -1) {
79870                                 var node = selectMenuItem(context, 'delete').node();
79871                                 if (!node) { return; }
79872                                 continueTo(multiDelete);
79873                         } else if (ids.length === 1 &&
79874                             ids.indexOf(_washingtonSegmentID) !== -1) {
79875                             return continueTo(multiSelect);
79876                         } else {
79877                             return continueTo(didSplit);
79878                         }
79879                     }, 300);  // after edit menu visible
79880                 });
79881
79882                 context.history().on('change.intro', function() {
79883                     if (!_washingtonSegmentID ||
79884                         !context.hasEntity(_washingtonSegmentID) ||
79885                         !context.hasEntity(washingtonStreetID) ||
79886                         !context.hasEntity(twelfthAvenueID) ||
79887                         !context.hasEntity(eleventhAvenueEndID)) {
79888                         return continueTo(rightClickIntersection);
79889                     }
79890                 });
79891
79892                 function continueTo(nextStep) {
79893                     context.map().on('move.intro drawn.intro', null);
79894                     context.ui().editMenu().on('toggled.intro', null);
79895                     context.history().on('change.intro', null);
79896                     nextStep();
79897                 }
79898             }
79899
79900
79901             function multiDelete() {
79902                 if (!_washingtonSegmentID ||
79903                     !context.hasEntity(_washingtonSegmentID) ||
79904                     !context.hasEntity(washingtonStreetID) ||
79905                     !context.hasEntity(twelfthAvenueID) ||
79906                     !context.hasEntity(eleventhAvenueEndID)) {
79907                     return continueTo(rightClickIntersection);
79908                 }
79909
79910                 var node = selectMenuItem(context, 'delete').node();
79911                 if (!node) { return continueTo(multiRightClick); }
79912
79913                 reveal('.edit-menu',
79914                     helpString('intro.lines.multi_delete'),
79915                     { padding: 50 }
79916                 );
79917
79918                 context.map().on('move.intro drawn.intro', function() {
79919                     reveal('.edit-menu',
79920                         helpString('intro.lines.multi_delete'),
79921                         { duration: 0, padding: 50 }
79922                     );
79923                 });
79924
79925                 context.on('exit.intro', function() {
79926                     if (context.hasEntity(_washingtonSegmentID) || context.hasEntity(twelfthAvenueID)) {
79927                         return continueTo(multiSelect);  // left select mode but roads still exist
79928                     }
79929                 });
79930
79931                 context.history().on('change.intro', function() {
79932                     if (context.hasEntity(_washingtonSegmentID) || context.hasEntity(twelfthAvenueID)) {
79933                         continueTo(retryDelete);         // changed something but roads still exist
79934                     } else {
79935                         continueTo(play);
79936                     }
79937                 });
79938
79939                 function continueTo(nextStep) {
79940                     context.map().on('move.intro drawn.intro', null);
79941                     context.on('exit.intro', null);
79942                     context.history().on('change.intro', null);
79943                     nextStep();
79944                 }
79945             }
79946
79947
79948             function retryDelete() {
79949                 context.enter(modeBrowse(context));
79950
79951                 var padding = 200 * Math.pow(2, context.map().zoom() - 18);
79952                 var box = pad(twelfthAvenue, padding, context);
79953                 reveal(box, helpString('intro.lines.retry_delete'), {
79954                     buttonText: _t('intro.ok'),
79955                     buttonCallback: function() { continueTo(multiSelect); }
79956                 });
79957
79958                 function continueTo(nextStep) {
79959                     nextStep();
79960                 }
79961             }
79962
79963
79964             function play() {
79965                 dispatch$1.call('done');
79966                 reveal('.ideditor',
79967                     helpString('intro.lines.play', { next: _t('intro.buildings.title') }), {
79968                         tooltipBox: '.intro-nav-wrap .chapter-building',
79969                         buttonText: _t('intro.ok'),
79970                         buttonCallback: function() { reveal('.ideditor'); }
79971                     }
79972                 );
79973            }
79974
79975
79976             chapter.enter = function() {
79977                 addLine();
79978             };
79979
79980
79981             chapter.exit = function() {
79982                 timeouts.forEach(window.clearTimeout);
79983                 select(window).on('pointerdown.intro mousedown.intro', null, true);
79984                 context.on('enter.intro exit.intro', null);
79985                 context.map().on('move.intro drawn.intro', null);
79986                 context.history().on('change.intro', null);
79987                 context.container().select('.inspector-wrap').on('wheel.intro', null);
79988                 context.container().select('.preset-list-button').on('click.intro', null);
79989             };
79990
79991
79992             chapter.restart = function() {
79993                 chapter.exit();
79994                 chapter.enter();
79995             };
79996
79997
79998             return utilRebind(chapter, dispatch$1, 'on');
79999         }
80000
80001         function uiIntroBuilding(context, reveal) {
80002             var dispatch$1 = dispatch('done');
80003             var house = [-85.62815, 41.95638];
80004             var tank = [-85.62732, 41.95347];
80005             var buildingCatetory = _mainPresetIndex.item('category-building');
80006             var housePreset = _mainPresetIndex.item('building/house');
80007             var tankPreset = _mainPresetIndex.item('man_made/storage_tank');
80008             var timeouts = [];
80009             var _houseID = null;
80010             var _tankID = null;
80011
80012
80013             var chapter = {
80014                 title: 'intro.buildings.title'
80015             };
80016
80017
80018             function timeout(f, t) {
80019                 timeouts.push(window.setTimeout(f, t));
80020             }
80021
80022
80023             function eventCancel() {
80024                 event.stopPropagation();
80025                 event.preventDefault();
80026             }
80027
80028
80029             function revealHouse(center, text, options) {
80030                 var padding = 160 * Math.pow(2, context.map().zoom() - 20);
80031                 var box = pad(center, padding, context);
80032                 reveal(box, text, options);
80033             }
80034
80035
80036             function revealTank(center, text, options) {
80037                 var padding = 190 * Math.pow(2, context.map().zoom() - 19.5);
80038                 var box = pad(center, padding, context);
80039                 reveal(box, text, options);
80040             }
80041
80042
80043             function addHouse() {
80044                 context.enter(modeBrowse(context));
80045                 context.history().reset('initial');
80046                 _houseID = null;
80047
80048                 var msec = transitionTime(house, context.map().center());
80049                 if (msec) { reveal(null, null, { duration: 0 }); }
80050                 context.map().centerZoomEase(house, 19, msec);
80051
80052                 timeout(function() {
80053                     var tooltip = reveal('button.add-area',
80054                         helpString('intro.buildings.add_building'));
80055
80056                     tooltip.selectAll('.popover-inner')
80057                         .insert('svg', 'span')
80058                         .attr('class', 'tooltip-illustration')
80059                         .append('use')
80060                         .attr('xlink:href', '#iD-graphic-buildings');
80061
80062                     context.on('enter.intro', function(mode) {
80063                         if (mode.id !== 'add-area') { return; }
80064                         continueTo(startHouse);
80065                     });
80066                 }, msec + 100);
80067
80068                 function continueTo(nextStep) {
80069                     context.on('enter.intro', null);
80070                     nextStep();
80071                 }
80072             }
80073
80074
80075             function startHouse() {
80076                 if (context.mode().id !== 'add-area') {
80077                     return continueTo(addHouse);
80078                 }
80079
80080                 _houseID = null;
80081                 context.map().zoomEase(20, 500);
80082
80083                 timeout(function() {
80084                     var startString = helpString('intro.buildings.start_building') +
80085                         helpString('intro.buildings.building_corner_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap'));
80086                     revealHouse(house, startString);
80087
80088                     context.map().on('move.intro drawn.intro', function() {
80089                         revealHouse(house, startString, { duration: 0 });
80090                     });
80091
80092                     context.on('enter.intro', function(mode) {
80093                         if (mode.id !== 'draw-area') { return chapter.restart(); }
80094                         continueTo(continueHouse);
80095                     });
80096
80097                 }, 550);  // after easing
80098
80099                 function continueTo(nextStep) {
80100                     context.map().on('move.intro drawn.intro', null);
80101                     context.on('enter.intro', null);
80102                     nextStep();
80103                 }
80104             }
80105
80106
80107             function continueHouse() {
80108                 if (context.mode().id !== 'draw-area') {
80109                     return continueTo(addHouse);
80110                 }
80111
80112                 _houseID = null;
80113
80114                 var continueString = helpString('intro.buildings.continue_building') + '{br}' +
80115                     helpString('intro.areas.finish_area_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap')) +
80116                     helpString('intro.buildings.finish_building');
80117
80118                 revealHouse(house, continueString);
80119
80120                 context.map().on('move.intro drawn.intro', function() {
80121                     revealHouse(house, continueString, { duration: 0 });
80122                 });
80123
80124                 context.on('enter.intro', function(mode) {
80125                     if (mode.id === 'draw-area') {
80126                         return;
80127                     } else if (mode.id === 'select') {
80128                         var graph = context.graph();
80129                         var way = context.entity(context.selectedIDs()[0]);
80130                         var nodes = graph.childNodes(way);
80131                         var points = utilArrayUniq(nodes)
80132                             .map(function(n) { return context.projection(n.loc); });
80133
80134                         if (isMostlySquare(points)) {
80135                             _houseID = way.id;
80136                             return continueTo(chooseCategoryBuilding);
80137                         } else {
80138                             return continueTo(retryHouse);
80139                         }
80140
80141                     } else {
80142                         return chapter.restart();
80143                     }
80144                 });
80145
80146                 function continueTo(nextStep) {
80147                     context.map().on('move.intro drawn.intro', null);
80148                     context.on('enter.intro', null);
80149                     nextStep();
80150                 }
80151             }
80152
80153
80154             function retryHouse() {
80155                 var onClick = function() { continueTo(addHouse); };
80156
80157                 revealHouse(house, helpString('intro.buildings.retry_building'),
80158                     { buttonText: _t('intro.ok'), buttonCallback: onClick }
80159                 );
80160
80161                 context.map().on('move.intro drawn.intro', function() {
80162                     revealHouse(house, helpString('intro.buildings.retry_building'),
80163                         { duration: 0, buttonText: _t('intro.ok'), buttonCallback: onClick }
80164                     );
80165                 });
80166
80167                 function continueTo(nextStep) {
80168                     context.map().on('move.intro drawn.intro', null);
80169                     nextStep();
80170                 }
80171             }
80172
80173
80174             function chooseCategoryBuilding() {
80175                 if (!_houseID || !context.hasEntity(_houseID)) {
80176                     return addHouse();
80177                 }
80178                 var ids = context.selectedIDs();
80179                 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _houseID) {
80180                     context.enter(modeSelect(context, [_houseID]));
80181                 }
80182
80183                 // disallow scrolling
80184                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
80185
80186                 timeout(function() {
80187                     // reset pane, in case user somehow happened to change it..
80188                     context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
80189
80190                     var button = context.container().select('.preset-category-building .preset-list-button');
80191
80192                     reveal(button.node(),
80193                         helpString('intro.buildings.choose_category_building', { category: buildingCatetory.name() })
80194                     );
80195
80196                     button.on('click.intro', function() {
80197                         button.on('click.intro', null);
80198                         continueTo(choosePresetHouse);
80199                     });
80200
80201                 }, 400);  // after preset list pane visible..
80202
80203
80204                 context.on('enter.intro', function(mode) {
80205                     if (!_houseID || !context.hasEntity(_houseID)) {
80206                         return continueTo(addHouse);
80207                     }
80208                     var ids = context.selectedIDs();
80209                     if (mode.id !== 'select' || !ids.length || ids[0] !== _houseID) {
80210                         return continueTo(chooseCategoryBuilding);
80211                     }
80212                 });
80213
80214                 function continueTo(nextStep) {
80215                     context.container().select('.inspector-wrap').on('wheel.intro', null);
80216                     context.container().select('.preset-list-button').on('click.intro', null);
80217                     context.on('enter.intro', null);
80218                     nextStep();
80219                 }
80220             }
80221
80222
80223             function choosePresetHouse() {
80224                 if (!_houseID || !context.hasEntity(_houseID)) {
80225                     return addHouse();
80226                 }
80227                 var ids = context.selectedIDs();
80228                 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _houseID) {
80229                     context.enter(modeSelect(context, [_houseID]));
80230                 }
80231
80232                 // disallow scrolling
80233                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
80234
80235                 timeout(function() {
80236                     // reset pane, in case user somehow happened to change it..
80237                     context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
80238
80239                     var button = context.container().select('.preset-building-house .preset-list-button');
80240
80241                     reveal(button.node(),
80242                         helpString('intro.buildings.choose_preset_house', { preset: housePreset.name() }),
80243                         { duration: 300 }
80244                     );
80245
80246                     button.on('click.intro', function() {
80247                         button.on('click.intro', null);
80248                         continueTo(closeEditorHouse);
80249                     });
80250
80251                 }, 400);  // after preset list pane visible..
80252
80253                 context.on('enter.intro', function(mode) {
80254                     if (!_houseID || !context.hasEntity(_houseID)) {
80255                         return continueTo(addHouse);
80256                     }
80257                     var ids = context.selectedIDs();
80258                     if (mode.id !== 'select' || !ids.length || ids[0] !== _houseID) {
80259                         return continueTo(chooseCategoryBuilding);
80260                     }
80261                 });
80262
80263                 function continueTo(nextStep) {
80264                     context.container().select('.inspector-wrap').on('wheel.intro', null);
80265                     context.container().select('.preset-list-button').on('click.intro', null);
80266                     context.on('enter.intro', null);
80267                     nextStep();
80268                 }
80269             }
80270
80271
80272             function closeEditorHouse() {
80273                 if (!_houseID || !context.hasEntity(_houseID)) {
80274                     return addHouse();
80275                 }
80276                 var ids = context.selectedIDs();
80277                 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _houseID) {
80278                     context.enter(modeSelect(context, [_houseID]));
80279                 }
80280
80281                 context.history().checkpoint('hasHouse');
80282
80283                 context.on('exit.intro', function() {
80284                     continueTo(rightClickHouse);
80285                 });
80286
80287                 timeout(function() {
80288                     reveal('.entity-editor-pane',
80289                         helpString('intro.buildings.close', { button: icon('#iD-icon-close', 'pre-text') })
80290                     );
80291                 }, 500);
80292
80293                 function continueTo(nextStep) {
80294                     context.on('exit.intro', null);
80295                     nextStep();
80296                 }
80297             }
80298
80299
80300             function rightClickHouse() {
80301                 if (!_houseID) { return chapter.restart(); }
80302
80303                 context.enter(modeBrowse(context));
80304                 context.history().reset('hasHouse');
80305                 var zoom = context.map().zoom();
80306                 if (zoom < 20) {
80307                     zoom = 20;
80308                 }
80309                 context.map().centerZoomEase(house, zoom, 500);
80310
80311                 context.on('enter.intro', function(mode) {
80312                     if (mode.id !== 'select') { return; }
80313                     var ids = context.selectedIDs();
80314                     if (ids.length !== 1 || ids[0] !== _houseID) { return; }
80315
80316                     timeout(function() {
80317                         var node = selectMenuItem(context, 'orthogonalize').node();
80318                         if (!node) { return; }
80319                         continueTo(clickSquare);
80320                     }, 50);  // after menu visible
80321                 });
80322
80323                 context.map().on('move.intro drawn.intro', function() {
80324                     var rightclickString = helpString('intro.buildings.' + (context.lastPointerType() === 'mouse' ? 'rightclick_building' : 'edit_menu_building_touch'));
80325                     revealHouse(house, rightclickString, { duration: 0 });
80326                 });
80327
80328                 context.history().on('change.intro', function() {
80329                     continueTo(rightClickHouse);
80330                 });
80331
80332                 function continueTo(nextStep) {
80333                     context.on('enter.intro', null);
80334                     context.map().on('move.intro drawn.intro', null);
80335                     context.history().on('change.intro', null);
80336                     nextStep();
80337                 }
80338             }
80339
80340
80341             function clickSquare() {
80342                 if (!_houseID) { return chapter.restart(); }
80343                 var entity = context.hasEntity(_houseID);
80344                 if (!entity) { return continueTo(rightClickHouse); }
80345
80346                 var node = selectMenuItem(context, 'orthogonalize').node();
80347                 if (!node) { return continueTo(rightClickHouse); }
80348
80349                 var wasChanged = false;
80350
80351                 reveal('.edit-menu',
80352                     helpString('intro.buildings.square_building'),
80353                     { padding: 50 }
80354                 );
80355
80356                 context.on('enter.intro', function(mode) {
80357                     if (mode.id === 'browse') {
80358                         continueTo(rightClickHouse);
80359                     } else if (mode.id === 'move' || mode.id === 'rotate') {
80360                         continueTo(retryClickSquare);
80361                     }
80362                 });
80363
80364                 context.map().on('move.intro', function() {
80365                     var node = selectMenuItem(context, 'orthogonalize').node();
80366                     if (!wasChanged && !node) { return continueTo(rightClickHouse); }
80367
80368                     reveal('.edit-menu',
80369                         helpString('intro.buildings.square_building'),
80370                         { duration: 0, padding: 50 }
80371                     );
80372                 });
80373
80374                 context.history().on('change.intro', function() {
80375                     wasChanged = true;
80376                     context.history().on('change.intro', null);
80377
80378                     // Something changed.  Wait for transition to complete and check undo annotation.
80379                     timeout(function() {
80380                         if (context.history().undoAnnotation() === _t('operations.orthogonalize.annotation.feature.single')) {
80381                             continueTo(doneSquare);
80382                         } else {
80383                             continueTo(retryClickSquare);
80384                         }
80385                     }, 500);  // after transitioned actions
80386                 });
80387
80388                 function continueTo(nextStep) {
80389                     context.on('enter.intro', null);
80390                     context.map().on('move.intro', null);
80391                     context.history().on('change.intro', null);
80392                     nextStep();
80393                 }
80394             }
80395
80396
80397             function retryClickSquare() {
80398                 context.enter(modeBrowse(context));
80399
80400                 revealHouse(house, helpString('intro.buildings.retry_square'), {
80401                     buttonText: _t('intro.ok'),
80402                     buttonCallback: function() { continueTo(rightClickHouse); }
80403                 });
80404
80405                 function continueTo(nextStep) {
80406                     nextStep();
80407                 }
80408             }
80409
80410
80411             function doneSquare() {
80412                 context.history().checkpoint('doneSquare');
80413
80414                 revealHouse(house, helpString('intro.buildings.done_square'), {
80415                     buttonText: _t('intro.ok'),
80416                     buttonCallback: function() { continueTo(addTank); }
80417                 });
80418
80419                 function continueTo(nextStep) {
80420                     nextStep();
80421                 }
80422             }
80423
80424
80425             function addTank() {
80426                 context.enter(modeBrowse(context));
80427                 context.history().reset('doneSquare');
80428                 _tankID = null;
80429
80430                 var msec = transitionTime(tank, context.map().center());
80431                 if (msec) { reveal(null, null, { duration: 0 }); }
80432                 context.map().centerZoomEase(tank, 19.5, msec);
80433
80434                 timeout(function() {
80435                     reveal('button.add-area',
80436                         helpString('intro.buildings.add_tank')
80437                     );
80438
80439                     context.on('enter.intro', function(mode) {
80440                         if (mode.id !== 'add-area') { return; }
80441                         continueTo(startTank);
80442                     });
80443                 }, msec + 100);
80444
80445                 function continueTo(nextStep) {
80446                     context.on('enter.intro', null);
80447                     nextStep();
80448                 }
80449             }
80450
80451
80452             function startTank() {
80453                 if (context.mode().id !== 'add-area') {
80454                     return continueTo(addTank);
80455                 }
80456
80457                 _tankID = null;
80458
80459                 timeout(function() {
80460                     var startString = helpString('intro.buildings.start_tank') +
80461                         helpString('intro.buildings.tank_edge_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap'));
80462                     revealTank(tank, startString);
80463
80464                     context.map().on('move.intro drawn.intro', function() {
80465                         revealTank(tank, startString, { duration: 0 });
80466                     });
80467
80468                     context.on('enter.intro', function(mode) {
80469                         if (mode.id !== 'draw-area') { return chapter.restart(); }
80470                         continueTo(continueTank);
80471                     });
80472
80473                 }, 550);  // after easing
80474
80475                 function continueTo(nextStep) {
80476                     context.map().on('move.intro drawn.intro', null);
80477                     context.on('enter.intro', null);
80478                     nextStep();
80479                 }
80480             }
80481
80482
80483             function continueTank() {
80484                 if (context.mode().id !== 'draw-area') {
80485                     return continueTo(addTank);
80486                 }
80487
80488                 _tankID = null;
80489
80490                 var continueString = helpString('intro.buildings.continue_tank') + '{br}' +
80491                     helpString('intro.areas.finish_area_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap')) +
80492                     helpString('intro.buildings.finish_tank');
80493
80494                 revealTank(tank, continueString);
80495
80496                 context.map().on('move.intro drawn.intro', function() {
80497                     revealTank(tank, continueString, { duration: 0 });
80498                 });
80499
80500                 context.on('enter.intro', function(mode) {
80501                     if (mode.id === 'draw-area') {
80502                         return;
80503                     } else if (mode.id === 'select') {
80504                         _tankID = context.selectedIDs()[0];
80505                         return continueTo(searchPresetTank);
80506                     } else {
80507                         return continueTo(addTank);
80508                     }
80509                 });
80510
80511                 function continueTo(nextStep) {
80512                     context.map().on('move.intro drawn.intro', null);
80513                     context.on('enter.intro', null);
80514                     nextStep();
80515                 }
80516             }
80517
80518
80519             function searchPresetTank() {
80520                 if (!_tankID || !context.hasEntity(_tankID)) {
80521                     return addTank();
80522                 }
80523                 var ids = context.selectedIDs();
80524                 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _tankID) {
80525                     context.enter(modeSelect(context, [_tankID]));
80526                 }
80527
80528                 // disallow scrolling
80529                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
80530
80531                 timeout(function() {
80532                     // reset pane, in case user somehow happened to change it..
80533                     context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
80534
80535                     context.container().select('.preset-search-input')
80536                         .on('keydown.intro', null)
80537                         .on('keyup.intro', checkPresetSearch);
80538
80539                     reveal('.preset-search-input',
80540                         helpString('intro.buildings.search_tank', { preset: tankPreset.name() })
80541                     );
80542                 }, 400);  // after preset list pane visible..
80543
80544                 context.on('enter.intro', function(mode) {
80545                     if (!_tankID || !context.hasEntity(_tankID)) {
80546                         return continueTo(addTank);
80547                     }
80548
80549                     var ids = context.selectedIDs();
80550                     if (mode.id !== 'select' || !ids.length || ids[0] !== _tankID) {
80551                         // keep the user's area selected..
80552                         context.enter(modeSelect(context, [_tankID]));
80553
80554                         // reset pane, in case user somehow happened to change it..
80555                         context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
80556                         // disallow scrolling
80557                         context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
80558
80559                         context.container().select('.preset-search-input')
80560                             .on('keydown.intro', null)
80561                             .on('keyup.intro', checkPresetSearch);
80562
80563                         reveal('.preset-search-input',
80564                             helpString('intro.buildings.search_tank', { preset: tankPreset.name() })
80565                         );
80566
80567                         context.history().on('change.intro', null);
80568                     }
80569                 });
80570
80571                 function checkPresetSearch() {
80572                     var first = context.container().select('.preset-list-item:first-child');
80573
80574                     if (first.classed('preset-man_made-storage_tank')) {
80575                         reveal(first.select('.preset-list-button').node(),
80576                             helpString('intro.buildings.choose_tank', { preset: tankPreset.name() }),
80577                             { duration: 300 }
80578                         );
80579
80580                         context.container().select('.preset-search-input')
80581                             .on('keydown.intro', eventCancel, true)
80582                             .on('keyup.intro', null);
80583
80584                         context.history().on('change.intro', function() {
80585                             continueTo(closeEditorTank);
80586                         });
80587                     }
80588                 }
80589
80590                 function continueTo(nextStep) {
80591                     context.container().select('.inspector-wrap').on('wheel.intro', null);
80592                     context.on('enter.intro', null);
80593                     context.history().on('change.intro', null);
80594                     context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
80595                     nextStep();
80596                 }
80597             }
80598
80599
80600             function closeEditorTank() {
80601                 if (!_tankID || !context.hasEntity(_tankID)) {
80602                     return addTank();
80603                 }
80604                 var ids = context.selectedIDs();
80605                 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _tankID) {
80606                     context.enter(modeSelect(context, [_tankID]));
80607                 }
80608
80609                 context.history().checkpoint('hasTank');
80610
80611                 context.on('exit.intro', function() {
80612                     continueTo(rightClickTank);
80613                 });
80614
80615                 timeout(function() {
80616                     reveal('.entity-editor-pane',
80617                         helpString('intro.buildings.close', { button: icon('#iD-icon-close', 'pre-text') })
80618                     );
80619                 }, 500);
80620
80621                 function continueTo(nextStep) {
80622                     context.on('exit.intro', null);
80623                     nextStep();
80624                 }
80625             }
80626
80627
80628             function rightClickTank() {
80629                 if (!_tankID) { return continueTo(addTank); }
80630
80631                 context.enter(modeBrowse(context));
80632                 context.history().reset('hasTank');
80633                 context.map().centerEase(tank, 500);
80634
80635                 timeout(function() {
80636                     context.on('enter.intro', function(mode) {
80637                         if (mode.id !== 'select') { return; }
80638                         var ids = context.selectedIDs();
80639                         if (ids.length !== 1 || ids[0] !== _tankID) { return; }
80640
80641                         timeout(function() {
80642                             var node = selectMenuItem(context, 'circularize').node();
80643                             if (!node) { return; }
80644                             continueTo(clickCircle);
80645                         }, 50);  // after menu visible
80646                     });
80647
80648                     var rightclickString = helpString('intro.buildings.' + (context.lastPointerType() === 'mouse' ? 'rightclick_tank' : 'edit_menu_tank_touch'));
80649
80650                     revealTank(tank, rightclickString);
80651
80652                     context.map().on('move.intro drawn.intro', function() {
80653                         revealTank(tank, rightclickString, { duration: 0 });
80654                     });
80655
80656                     context.history().on('change.intro', function() {
80657                         continueTo(rightClickTank);
80658                     });
80659
80660                 }, 600);
80661
80662                 function continueTo(nextStep) {
80663                     context.on('enter.intro', null);
80664                     context.map().on('move.intro drawn.intro', null);
80665                     context.history().on('change.intro', null);
80666                     nextStep();
80667                 }
80668             }
80669
80670
80671             function clickCircle() {
80672                 if (!_tankID) { return chapter.restart(); }
80673                 var entity = context.hasEntity(_tankID);
80674                 if (!entity) { return continueTo(rightClickTank); }
80675
80676                 var node = selectMenuItem(context, 'circularize').node();
80677                 if (!node) { return continueTo(rightClickTank); }
80678
80679                 var wasChanged = false;
80680
80681                 reveal('.edit-menu',
80682                     helpString('intro.buildings.circle_tank'),
80683                     { padding: 50 }
80684                 );
80685
80686                 context.on('enter.intro', function(mode) {
80687                     if (mode.id === 'browse') {
80688                         continueTo(rightClickTank);
80689                     } else if (mode.id === 'move' || mode.id === 'rotate') {
80690                         continueTo(retryClickCircle);
80691                     }
80692                 });
80693
80694                 context.map().on('move.intro', function() {
80695                     var node = selectMenuItem(context, 'circularize').node();
80696                     if (!wasChanged && !node) { return continueTo(rightClickTank); }
80697
80698                     reveal('.edit-menu',
80699                         helpString('intro.buildings.circle_tank'),
80700                         { duration: 0, padding: 50 }
80701                     );
80702                 });
80703
80704                 context.history().on('change.intro', function() {
80705                     wasChanged = true;
80706                     context.history().on('change.intro', null);
80707
80708                     // Something changed.  Wait for transition to complete and check undo annotation.
80709                     timeout(function() {
80710                         if (context.history().undoAnnotation() === _t('operations.circularize.annotation.single')) {
80711                             continueTo(play);
80712                         } else {
80713                             continueTo(retryClickCircle);
80714                         }
80715                     }, 500);  // after transitioned actions
80716                 });
80717
80718                 function continueTo(nextStep) {
80719                     context.on('enter.intro', null);
80720                     context.map().on('move.intro', null);
80721                     context.history().on('change.intro', null);
80722                     nextStep();
80723                 }
80724             }
80725
80726
80727             function retryClickCircle() {
80728                 context.enter(modeBrowse(context));
80729
80730                 revealTank(tank, helpString('intro.buildings.retry_circle'), {
80731                     buttonText: _t('intro.ok'),
80732                     buttonCallback: function() { continueTo(rightClickTank); }
80733                 });
80734
80735                 function continueTo(nextStep) {
80736                     nextStep();
80737                 }
80738             }
80739
80740
80741             function play() {
80742                 dispatch$1.call('done');
80743                 reveal('.ideditor',
80744                     helpString('intro.buildings.play', { next: _t('intro.startediting.title') }), {
80745                         tooltipBox: '.intro-nav-wrap .chapter-startEditing',
80746                         buttonText: _t('intro.ok'),
80747                         buttonCallback: function() { reveal('.ideditor'); }
80748                     }
80749                 );
80750             }
80751
80752
80753             chapter.enter = function() {
80754                 addHouse();
80755             };
80756
80757
80758             chapter.exit = function() {
80759                 timeouts.forEach(window.clearTimeout);
80760                 context.on('enter.intro exit.intro', null);
80761                 context.map().on('move.intro drawn.intro', null);
80762                 context.history().on('change.intro', null);
80763                 context.container().select('.inspector-wrap').on('wheel.intro', null);
80764                 context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
80765                 context.container().select('.more-fields .combobox-input').on('click.intro', null);
80766             };
80767
80768
80769             chapter.restart = function() {
80770                 chapter.exit();
80771                 chapter.enter();
80772             };
80773
80774
80775             return utilRebind(chapter, dispatch$1, 'on');
80776         }
80777
80778         function uiIntroStartEditing(context, reveal) {
80779             var dispatch$1 = dispatch('done', 'startEditing');
80780             var modalSelection = select(null);
80781
80782
80783             var chapter = {
80784                 title: 'intro.startediting.title'
80785             };
80786
80787             function showHelp() {
80788                 reveal('.map-control.help-control',
80789                     helpString('intro.startediting.help'), {
80790                         buttonText: _t('intro.ok'),
80791                         buttonCallback: function() { shortcuts(); }
80792                     }
80793                 );
80794             }
80795
80796             function shortcuts() {
80797                 reveal('.map-control.help-control',
80798                     helpString('intro.startediting.shortcuts'), {
80799                         buttonText: _t('intro.ok'),
80800                         buttonCallback: function() { showSave(); }
80801                     }
80802                 );
80803             }
80804
80805             function showSave() {
80806                 context.container().selectAll('.shaded').remove();  // in case user opened keyboard shortcuts
80807                 reveal('.top-toolbar button.save',
80808                     helpString('intro.startediting.save'), {
80809                         buttonText: _t('intro.ok'),
80810                         buttonCallback: function() { showStart(); }
80811                     }
80812                 );
80813             }
80814
80815             function showStart() {
80816                 context.container().selectAll('.shaded').remove();  // in case user opened keyboard shortcuts
80817
80818                 modalSelection = uiModal(context.container());
80819
80820                 modalSelection.select('.modal')
80821                     .attr('class', 'modal-splash modal');
80822
80823                 modalSelection.selectAll('.close').remove();
80824
80825                 var startbutton = modalSelection.select('.content')
80826                     .attr('class', 'fillL')
80827                     .append('button')
80828                         .attr('class', 'modal-section huge-modal-button')
80829                         .on('click', function() {
80830                             modalSelection.remove();
80831                         });
80832
80833                     startbutton
80834                         .append('svg')
80835                         .attr('class', 'illustration')
80836                         .append('use')
80837                         .attr('xlink:href', '#iD-logo-walkthrough');
80838
80839                     startbutton
80840                         .append('h2')
80841                         .text(_t('intro.startediting.start'));
80842
80843                 dispatch$1.call('startEditing');
80844             }
80845
80846
80847             chapter.enter = function() {
80848                 showHelp();
80849             };
80850
80851
80852             chapter.exit = function() {
80853                 modalSelection.remove();
80854                 context.container().selectAll('.shaded').remove();  // in case user opened keyboard shortcuts
80855             };
80856
80857
80858             return utilRebind(chapter, dispatch$1, 'on');
80859         }
80860
80861         var chapterUi = {
80862           welcome: uiIntroWelcome,
80863           navigation: uiIntroNavigation,
80864           point: uiIntroPoint,
80865           area: uiIntroArea,
80866           line: uiIntroLine,
80867           building: uiIntroBuilding,
80868           startEditing: uiIntroStartEditing
80869         };
80870
80871         var chapterFlow = [
80872           'welcome',
80873           'navigation',
80874           'point',
80875           'area',
80876           'line',
80877           'building',
80878           'startEditing'
80879         ];
80880
80881
80882         function uiIntro(context) {
80883           var INTRO_IMAGERY = 'EsriWorldImageryClarity';
80884           var _introGraph = {};
80885           var _currChapter;
80886
80887
80888           function intro(selection) {
80889             _mainFileFetcher.get('intro_graph')
80890               .then(function (dataIntroGraph) {
80891                 // create entities for intro graph and localize names
80892                 for (var id in dataIntroGraph) {
80893                   if (!_introGraph[id]) {
80894                     _introGraph[id] = osmEntity(localize(dataIntroGraph[id]));
80895                   }
80896                 }
80897                 selection.call(startIntro);
80898               })
80899               .catch(function() { /* ignore */ });
80900           }
80901
80902
80903           function startIntro(selection) {
80904             context.enter(modeBrowse(context));
80905
80906             // Save current map state
80907             var osm = context.connection();
80908             var history = context.history().toJSON();
80909             var hash = window.location.hash;
80910             var center = context.map().center();
80911             var zoom = context.map().zoom();
80912             var background = context.background().baseLayerSource();
80913             var overlays = context.background().overlayLayerSources();
80914             var opacity = context.container().selectAll('.main-map .layer-background').style('opacity');
80915             var caches = osm && osm.caches();
80916             var baseEntities = context.history().graph().base().entities;
80917
80918             // Show sidebar and disable the sidebar resizing button
80919             // (this needs to be before `context.inIntro(true)`)
80920             context.ui().sidebar.expand();
80921             context.container().selectAll('button.sidebar-toggle').classed('disabled', true);
80922
80923             // Block saving
80924             context.inIntro(true);
80925
80926             // Load semi-real data used in intro
80927             if (osm) { osm.toggle(false).reset(); }
80928             context.history().reset();
80929             context.history().merge(Object.values(coreGraph().load(_introGraph).entities));
80930             context.history().checkpoint('initial');
80931
80932             // Setup imagery
80933             var imagery = context.background().findSource(INTRO_IMAGERY);
80934             if (imagery) {
80935               context.background().baseLayerSource(imagery);
80936             } else {
80937               context.background().bing();
80938             }
80939             overlays.forEach(function (d) { return context.background().toggleOverlayLayer(d); });
80940
80941             // Setup data layers (only OSM)
80942             var layers = context.layers();
80943             layers.all().forEach(function (item) {
80944               // if the layer has the function `enabled`
80945               if (typeof item.layer.enabled === 'function') {
80946                 item.layer.enabled(item.id === 'osm');
80947               }
80948             });
80949
80950
80951             context.container().selectAll('.main-map .layer-background').style('opacity', 1);
80952
80953             var curtain = uiCurtain(context.container().node());
80954             selection.call(curtain);
80955
80956             // Store that the user started the walkthrough..
80957             corePreferences('walkthrough_started', 'yes');
80958
80959             // Restore previous walkthrough progress..
80960             var storedProgress = corePreferences('walkthrough_progress') || '';
80961             var progress = storedProgress.split(';').filter(Boolean);
80962
80963             var chapters = chapterFlow.map(function (chapter, i) {
80964               var s = chapterUi[chapter](context, curtain.reveal)
80965                 .on('done', function () {
80966
80967                   buttons
80968                     .filter(function (d) { return d.title === s.title; })
80969                     .classed('finished', true);
80970
80971                   if (i < chapterFlow.length - 1) {
80972                     var next = chapterFlow[i + 1];
80973                     context.container().select(("button.chapter-" + next))
80974                       .classed('next', true);
80975                   }
80976
80977                   // Store walkthrough progress..
80978                   progress.push(chapter);
80979                   corePreferences('walkthrough_progress', utilArrayUniq(progress).join(';'));
80980                 });
80981               return s;
80982             });
80983
80984             chapters[chapters.length - 1].on('startEditing', function () {
80985               // Store walkthrough progress..
80986               progress.push('startEditing');
80987               corePreferences('walkthrough_progress', utilArrayUniq(progress).join(';'));
80988
80989               // Store if walkthrough is completed..
80990               var incomplete = utilArrayDifference(chapterFlow, progress);
80991               if (!incomplete.length) {
80992                 corePreferences('walkthrough_completed', 'yes');
80993               }
80994
80995               curtain.remove();
80996               navwrap.remove();
80997               context.container().selectAll('.main-map .layer-background').style('opacity', opacity);
80998               context.container().selectAll('button.sidebar-toggle').classed('disabled', false);
80999               if (osm) { osm.toggle(true).reset().caches(caches); }
81000               context.history().reset().merge(Object.values(baseEntities));
81001               context.background().baseLayerSource(background);
81002               overlays.forEach(function (d) { return context.background().toggleOverlayLayer(d); });
81003               if (history) { context.history().fromJSON(history, false); }
81004               context.map().centerZoom(center, zoom);
81005               window.location.replace(hash);
81006               context.inIntro(false);
81007             });
81008
81009             var navwrap = selection
81010               .append('div')
81011               .attr('class', 'intro-nav-wrap fillD');
81012
81013             navwrap
81014               .append('svg')
81015               .attr('class', 'intro-nav-wrap-logo')
81016               .append('use')
81017               .attr('xlink:href', '#iD-logo-walkthrough');
81018
81019             var buttonwrap = navwrap
81020               .append('div')
81021               .attr('class', 'joined')
81022               .selectAll('button.chapter');
81023
81024             var buttons = buttonwrap
81025               .data(chapters)
81026               .enter()
81027               .append('button')
81028               .attr('class', function (d, i) { return ("chapter chapter-" + (chapterFlow[i])); })
81029               .on('click', enterChapter);
81030
81031             buttons
81032               .append('span')
81033               .text(function (d) { return _t(d.title); });
81034
81035             buttons
81036               .append('span')
81037               .attr('class', 'status')
81038               .call(svgIcon((_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward'), 'inline'));
81039
81040             enterChapter(chapters[0]);
81041
81042
81043             function enterChapter(newChapter) {
81044               if (_currChapter) { _currChapter.exit(); }
81045               context.enter(modeBrowse(context));
81046
81047               _currChapter = newChapter;
81048               _currChapter.enter();
81049
81050               buttons
81051                 .classed('next', false)
81052                 .classed('active', function (d) { return d.title === _currChapter.title; });
81053             }
81054           }
81055
81056
81057           return intro;
81058         }
81059
81060         function uiIssuesInfo(context) {
81061
81062             var warningsItem = {
81063                 id: 'warnings',
81064                 count: 0,
81065                 iconID: 'iD-icon-alert',
81066                 descriptionID: 'issues.warnings_and_errors'
81067             };
81068
81069             var resolvedItem = {
81070                 id: 'resolved',
81071                 count: 0,
81072                 iconID: 'iD-icon-apply',
81073                 descriptionID: 'issues.user_resolved_issues'
81074             };
81075
81076             function update(selection) {
81077
81078                 var shownItems = [];
81079
81080                 var liveIssues = context.validator().getIssues({
81081                     what: corePreferences('validate-what') || 'edited',
81082                     where: corePreferences('validate-where') || 'all'
81083                 });
81084                 if (liveIssues.length) {
81085                     warningsItem.count = liveIssues.length;
81086                     shownItems.push(warningsItem);
81087                 }
81088
81089                 if (corePreferences('validate-what') === 'all') {
81090                     var resolvedIssues = context.validator().getResolvedIssues();
81091                     if (resolvedIssues.length) {
81092                         resolvedItem.count = resolvedIssues.length;
81093                         shownItems.push(resolvedItem);
81094                     }
81095                 }
81096
81097                 var chips = selection.selectAll('.chip')
81098                     .data(shownItems, function(d) {
81099                         return d.id;
81100                     });
81101
81102                 chips.exit().remove();
81103
81104                 var enter = chips.enter()
81105                     .append('a')
81106                     .attr('class', function(d) {
81107                         return 'chip ' + d.id + '-count';
81108                     })
81109                     .attr('href', '#')
81110                     .attr('tabindex', -1)
81111                     .each(function(d) {
81112
81113                         var chipSelection = select(this);
81114
81115                         var tooltipBehavior = uiTooltip()
81116                             .placement('top')
81117                             .title(_t(d.descriptionID));
81118
81119                         chipSelection
81120                             .call(tooltipBehavior)
81121                             .on('click', function() {
81122                                 event.preventDefault();
81123
81124                                 tooltipBehavior.hide(select(this));
81125                                 // open the Issues pane
81126                                 context.ui().togglePanes(context.container().select('.map-panes .issues-pane'));
81127                             });
81128
81129                         chipSelection.call(svgIcon('#' + d.iconID));
81130
81131                     });
81132
81133                 enter.append('span')
81134                     .attr('class', 'count');
81135
81136                 enter.merge(chips)
81137                     .selectAll('span.count')
81138                     .text(function(d) {
81139                         return d.count.toString();
81140                     });
81141             }
81142
81143
81144             return function(selection) {
81145                 update(selection);
81146
81147                 context.validator().on('validated.infobox', function() {
81148                     update(selection);
81149                 });
81150             };
81151         }
81152
81153         // import { utilGetDimensions } from '../util/dimensions';
81154
81155
81156         function uiMapInMap(context) {
81157
81158             function mapInMap(selection) {
81159                 var backgroundLayer = rendererTileLayer(context);
81160                 var overlayLayers = {};
81161                 var projection = geoRawMercator();
81162                 var dataLayer = svgData(projection, context).showLabels(false);
81163                 var debugLayer = svgDebug(projection, context);
81164                 var zoom = d3_zoom()
81165                     .scaleExtent([geoZoomToScale(0.5), geoZoomToScale(24)])
81166                     .on('start', zoomStarted)
81167                     .on('zoom', zoomed)
81168                     .on('end', zoomEnded);
81169
81170                 var wrap = select(null);
81171                 var tiles = select(null);
81172                 var viewport = select(null);
81173
81174                 var _isTransformed = false;
81175                 var _isHidden = true;
81176                 var _skipEvents = false;
81177                 var _gesture = null;
81178                 var _zDiff = 6;    // by default, minimap renders at (main zoom - 6)
81179                 var _dMini;        // dimensions of minimap
81180                 var _cMini;        // center pixel of minimap
81181                 var _tStart;       // transform at start of gesture
81182                 var _tCurr;        // transform at most recent event
81183                 var _timeoutID;
81184
81185
81186                 function zoomStarted() {
81187                     if (_skipEvents) { return; }
81188                     _tStart = _tCurr = projection.transform();
81189                     _gesture = null;
81190                 }
81191
81192
81193                 function zoomed() {
81194                     if (_skipEvents) { return; }
81195
81196                     var x = event.transform.x;
81197                     var y = event.transform.y;
81198                     var k = event.transform.k;
81199                     var isZooming = (k !== _tStart.k);
81200                     var isPanning = (x !== _tStart.x || y !== _tStart.y);
81201
81202                     if (!isZooming && !isPanning) {
81203                         return;  // no change
81204                     }
81205
81206                     // lock in either zooming or panning, don't allow both in minimap.
81207                     if (!_gesture) {
81208                         _gesture = isZooming ? 'zoom' : 'pan';
81209                     }
81210
81211                     var tMini = projection.transform();
81212                     var tX, tY, scale;
81213
81214                     if (_gesture === 'zoom') {
81215                         scale = k / tMini.k;
81216                         tX = (_cMini[0] / scale - _cMini[0]) * scale;
81217                         tY = (_cMini[1] / scale - _cMini[1]) * scale;
81218                     } else {
81219                         k = tMini.k;
81220                         scale = 1;
81221                         tX = x - tMini.x;
81222                         tY = y - tMini.y;
81223                     }
81224
81225                     utilSetTransform(tiles, tX, tY, scale);
81226                     utilSetTransform(viewport, 0, 0, scale);
81227                     _isTransformed = true;
81228                     _tCurr = identity$2.translate(x, y).scale(k);
81229
81230                     var zMain = geoScaleToZoom(context.projection.scale());
81231                     var zMini = geoScaleToZoom(k);
81232
81233                     _zDiff = zMain - zMini;
81234
81235                     queueRedraw();
81236                 }
81237
81238
81239                 function zoomEnded() {
81240                     if (_skipEvents) { return; }
81241                     if (_gesture !== 'pan') { return; }
81242
81243                     updateProjection();
81244                     _gesture = null;
81245                     context.map().center(projection.invert(_cMini));   // recenter main map..
81246                 }
81247
81248
81249                 function updateProjection() {
81250                     var loc = context.map().center();
81251                     var tMain = context.projection.transform();
81252                     var zMain = geoScaleToZoom(tMain.k);
81253                     var zMini = Math.max(zMain - _zDiff, 0.5);
81254                     var kMini = geoZoomToScale(zMini);
81255
81256                     projection
81257                         .translate([tMain.x, tMain.y])
81258                         .scale(kMini);
81259
81260                     var point = projection(loc);
81261                     var mouse = (_gesture === 'pan') ? geoVecSubtract([_tCurr.x, _tCurr.y], [_tStart.x, _tStart.y]) : [0, 0];
81262                     var xMini = _cMini[0] - point[0] + tMain.x + mouse[0];
81263                     var yMini = _cMini[1] - point[1] + tMain.y + mouse[1];
81264
81265                     projection
81266                         .translate([xMini, yMini])
81267                         .clipExtent([[0, 0], _dMini]);
81268
81269                     _tCurr = projection.transform();
81270
81271                     if (_isTransformed) {
81272                         utilSetTransform(tiles, 0, 0);
81273                         utilSetTransform(viewport, 0, 0);
81274                         _isTransformed = false;
81275                     }
81276
81277                     zoom
81278                         .scaleExtent([geoZoomToScale(0.5), geoZoomToScale(zMain - 3)]);
81279
81280                     _skipEvents = true;
81281                     wrap.call(zoom.transform, _tCurr);
81282                     _skipEvents = false;
81283                 }
81284
81285
81286                 function redraw() {
81287                     clearTimeout(_timeoutID);
81288                     if (_isHidden) { return; }
81289
81290                     updateProjection();
81291                     var zMini = geoScaleToZoom(projection.scale());
81292
81293                     // setup tile container
81294                     tiles = wrap
81295                         .selectAll('.map-in-map-tiles')
81296                         .data([0]);
81297
81298                     tiles = tiles.enter()
81299                         .append('div')
81300                         .attr('class', 'map-in-map-tiles')
81301                         .merge(tiles);
81302
81303                     // redraw background
81304                     backgroundLayer
81305                         .source(context.background().baseLayerSource())
81306                         .projection(projection)
81307                         .dimensions(_dMini);
81308
81309                     var background = tiles
81310                         .selectAll('.map-in-map-background')
81311                         .data([0]);
81312
81313                     background.enter()
81314                         .append('div')
81315                         .attr('class', 'map-in-map-background')
81316                         .merge(background)
81317                         .call(backgroundLayer);
81318
81319
81320                     // redraw overlay
81321                     var overlaySources = context.background().overlayLayerSources();
81322                     var activeOverlayLayers = [];
81323                     for (var i = 0; i < overlaySources.length; i++) {
81324                         if (overlaySources[i].validZoom(zMini)) {
81325                             if (!overlayLayers[i]) { overlayLayers[i] = rendererTileLayer(context); }
81326                             activeOverlayLayers.push(overlayLayers[i]
81327                                 .source(overlaySources[i])
81328                                 .projection(projection)
81329                                 .dimensions(_dMini));
81330                         }
81331                     }
81332
81333                     var overlay = tiles
81334                         .selectAll('.map-in-map-overlay')
81335                         .data([0]);
81336
81337                     overlay = overlay.enter()
81338                         .append('div')
81339                         .attr('class', 'map-in-map-overlay')
81340                         .merge(overlay);
81341
81342
81343                     var overlays = overlay
81344                         .selectAll('div')
81345                         .data(activeOverlayLayers, function(d) { return d.source().name(); });
81346
81347                     overlays.exit()
81348                         .remove();
81349
81350                     overlays = overlays.enter()
81351                         .append('div')
81352                         .merge(overlays)
81353                         .each(function(layer) { select(this).call(layer); });
81354
81355
81356                     var dataLayers = tiles
81357                         .selectAll('.map-in-map-data')
81358                         .data([0]);
81359
81360                     dataLayers.exit()
81361                         .remove();
81362
81363                     dataLayers = dataLayers.enter()
81364                         .append('svg')
81365                         .attr('class', 'map-in-map-data')
81366                         .merge(dataLayers)
81367                         .call(dataLayer)
81368                         .call(debugLayer);
81369
81370
81371                     // redraw viewport bounding box
81372                     if (_gesture !== 'pan') {
81373                         var getPath = d3_geoPath(projection);
81374                         var bbox = { type: 'Polygon', coordinates: [context.map().extent().polygon()] };
81375
81376                         viewport = wrap.selectAll('.map-in-map-viewport')
81377                             .data([0]);
81378
81379                         viewport = viewport.enter()
81380                             .append('svg')
81381                             .attr('class', 'map-in-map-viewport')
81382                             .merge(viewport);
81383
81384
81385                         var path = viewport.selectAll('.map-in-map-bbox')
81386                             .data([bbox]);
81387
81388                         path.enter()
81389                             .append('path')
81390                             .attr('class', 'map-in-map-bbox')
81391                             .merge(path)
81392                             .attr('d', getPath)
81393                             .classed('thick', function(d) { return getPath.area(d) < 30; });
81394                     }
81395                 }
81396
81397
81398                 function queueRedraw() {
81399                     clearTimeout(_timeoutID);
81400                     _timeoutID = setTimeout(function() { redraw(); }, 750);
81401                 }
81402
81403
81404                 function toggle() {
81405                     if (event) { event.preventDefault(); }
81406
81407                     _isHidden = !_isHidden;
81408
81409                     context.container().select('.minimap-toggle-item')
81410                         .classed('active', !_isHidden)
81411                         .select('input')
81412                         .property('checked', !_isHidden);
81413
81414                     if (_isHidden) {
81415                         wrap
81416                             .style('display', 'block')
81417                             .style('opacity', '1')
81418                             .transition()
81419                             .duration(200)
81420                             .style('opacity', '0')
81421                             .on('end', function() {
81422                                 selection.selectAll('.map-in-map')
81423                                     .style('display', 'none');
81424                             });
81425                     } else {
81426                         wrap
81427                             .style('display', 'block')
81428                             .style('opacity', '0')
81429                             .transition()
81430                             .duration(200)
81431                             .style('opacity', '1')
81432                             .on('end', function() {
81433                                 redraw();
81434                             });
81435                     }
81436                 }
81437
81438
81439                 uiMapInMap.toggle = toggle;
81440
81441                 wrap = selection.selectAll('.map-in-map')
81442                     .data([0]);
81443
81444                 wrap = wrap.enter()
81445                     .append('div')
81446                     .attr('class', 'map-in-map')
81447                     .style('display', (_isHidden ? 'none' : 'block'))
81448                     .call(zoom)
81449                     .on('dblclick.zoom', null)
81450                     .merge(wrap);
81451
81452                 // reflow warning: Hardcode dimensions - currently can't resize it anyway..
81453                 _dMini = [200,150]; //utilGetDimensions(wrap);
81454                 _cMini = geoVecScale(_dMini, 0.5);
81455
81456                 context.map()
81457                     .on('drawn.map-in-map', function(drawn) {
81458                         if (drawn.full === true) {
81459                             redraw();
81460                         }
81461                     });
81462
81463                 redraw();
81464
81465                 context.keybinding()
81466                     .on(_t('background.minimap.key'), toggle);
81467             }
81468
81469             return mapInMap;
81470         }
81471
81472         function uiNotice(context) {
81473
81474             return function(selection) {
81475                 var div = selection
81476                     .append('div')
81477                     .attr('class', 'notice');
81478
81479                 var button = div
81480                     .append('button')
81481                     .attr('class', 'zoom-to notice fillD')
81482                     .on('click', function() {
81483                         context.map().zoomEase(context.minEditableZoom());
81484                     })
81485                     .on('wheel', function() {   // let wheel events pass through #4482
81486                         var e2 = new WheelEvent(event.type, event);
81487                         context.surface().node().dispatchEvent(e2);
81488                     });
81489
81490                 button
81491                     .call(svgIcon('#iD-icon-plus', 'pre-text'))
81492                     .append('span')
81493                     .attr('class', 'label')
81494                     .text(_t('zoom_in_edit'));
81495
81496
81497                 function disableTooHigh() {
81498                     var canEdit = context.map().zoom() >= context.minEditableZoom();
81499                     div.style('display', canEdit ? 'none' : 'block');
81500                 }
81501
81502                 context.map()
81503                     .on('move.notice', debounce(disableTooHigh, 500));
81504
81505                 disableTooHigh();
81506             };
81507         }
81508
81509         function uiPhotoviewer(context) {
81510
81511             var dispatch$1 = dispatch('resize');
81512
81513             var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
81514
81515             function photoviewer(selection) {
81516                 selection
81517                     .append('button')
81518                     .attr('class', 'thumb-hide')
81519                     .on('click', function () {
81520                         if (services.streetside) { services.streetside.hideViewer(context); }
81521                         if (services.mapillary) { services.mapillary.hideViewer(context); }
81522                         if (services.openstreetcam) { services.openstreetcam.hideViewer(context); }
81523                     })
81524                     .append('div')
81525                     .call(svgIcon('#iD-icon-close'));
81526
81527                 function preventDefault() {
81528                     event.preventDefault();
81529                 }
81530
81531                 selection
81532                     .append('button')
81533                     .attr('class', 'resize-handle-xy')
81534                     .on('touchstart touchdown touchend', preventDefault)
81535                     .on(
81536                         _pointerPrefix + 'down',
81537                         buildResizeListener(selection, 'resize', dispatch$1, { resizeOnX: true, resizeOnY: true })
81538                     );
81539
81540                 selection
81541                     .append('button')
81542                     .attr('class', 'resize-handle-x')
81543                     .on('touchstart touchdown touchend', preventDefault)
81544                     .on(
81545                         _pointerPrefix + 'down',
81546                         buildResizeListener(selection, 'resize', dispatch$1, { resizeOnX: true })
81547                     );
81548
81549                 selection
81550                     .append('button')
81551                     .attr('class', 'resize-handle-y')
81552                     .on('touchstart touchdown touchend', preventDefault)
81553                     .on(
81554                         _pointerPrefix + 'down',
81555                         buildResizeListener(selection, 'resize', dispatch$1, { resizeOnY: true })
81556                     );
81557
81558                 services.streetside.loadViewer(context);
81559                 services.mapillary.loadViewer(context);
81560                 services.openstreetcam.loadViewer(context);
81561
81562                 function buildResizeListener(target, eventName, dispatch, options) {
81563
81564                     var resizeOnX = !!options.resizeOnX;
81565                     var resizeOnY = !!options.resizeOnY;
81566                     var minHeight = options.minHeight || 240;
81567                     var minWidth = options.minWidth || 320;
81568                     var pointerId;
81569                     var startX;
81570                     var startY;
81571                     var startWidth;
81572                     var startHeight;
81573
81574                     function startResize() {
81575                         if (pointerId !== (event.pointerId || 'mouse')) { return; }
81576
81577                         event.preventDefault();
81578                         event.stopPropagation();
81579
81580                         var mapSize = context.map().dimensions();
81581
81582                         if (resizeOnX) {
81583                             var maxWidth = mapSize[0];
81584                             var newWidth = clamp((startWidth + event.clientX - startX), minWidth, maxWidth);
81585                             target.style('width', newWidth + 'px');
81586                         }
81587
81588                         if (resizeOnY) {
81589                             var maxHeight = mapSize[1] - 90;  // preserve space at top/bottom of map
81590                             var newHeight = clamp((startHeight + startY - event.clientY), minHeight, maxHeight);
81591                             target.style('height', newHeight + 'px');
81592                         }
81593
81594                         dispatch.call(eventName, target, utilGetDimensions(target, true));
81595                     }
81596
81597                     function clamp(num, min, max) {
81598                         return Math.max(min, Math.min(num, max));
81599                     }
81600
81601                     function stopResize() {
81602                         if (pointerId !== (event.pointerId || 'mouse')) { return; }
81603
81604                         event.preventDefault();
81605                         event.stopPropagation();
81606
81607                         // remove all the listeners we added
81608                         select(window)
81609                             .on('.' + eventName, null);
81610                     }
81611
81612                     return function initResize() {
81613                         event.preventDefault();
81614                         event.stopPropagation();
81615
81616                         pointerId = event.pointerId || 'mouse';
81617
81618                         startX = event.clientX;
81619                         startY = event.clientY;
81620                         var targetRect = target.node().getBoundingClientRect();
81621                         startWidth = targetRect.width;
81622                         startHeight = targetRect.height;
81623
81624                         select(window)
81625                             .on(_pointerPrefix + 'move.' + eventName, startResize, false)
81626                             .on(_pointerPrefix + 'up.' + eventName, stopResize, false);
81627
81628                         if (_pointerPrefix === 'pointer') {
81629                             select(window)
81630                                 .on('pointercancel.' + eventName, stopResize, false);
81631                         }
81632                     };
81633                 }
81634             }
81635
81636             photoviewer.onMapResize = function() {
81637                 var photoviewer = context.container().select('.photoviewer');
81638                 var content = context.container().select('.main-content');
81639                 var mapDimensions = utilGetDimensions(content, true);
81640                 // shrink photo viewer if it is too big
81641                 // (-90 preserves space at top and bottom of map used by menus)
81642                 var photoDimensions = utilGetDimensions(photoviewer, true);
81643                 if (photoDimensions[0] > mapDimensions[0] || photoDimensions[1] > (mapDimensions[1] - 90)) {
81644                     var setPhotoDimensions = [
81645                         Math.min(photoDimensions[0], mapDimensions[0]),
81646                         Math.min(photoDimensions[1], mapDimensions[1] - 90) ];
81647
81648                     photoviewer
81649                         .style('width', setPhotoDimensions[0] + 'px')
81650                         .style('height', setPhotoDimensions[1] + 'px');
81651
81652                     dispatch$1.call('resize', photoviewer, setPhotoDimensions);
81653                 }
81654             };
81655
81656             return utilRebind(photoviewer, dispatch$1, 'on');
81657         }
81658
81659         function uiRestore(context) {
81660           return function(selection) {
81661             if (!context.history().hasRestorableChanges()) { return; }
81662
81663             var modalSelection = uiModal(selection, true);
81664
81665             modalSelection.select('.modal')
81666               .attr('class', 'modal fillL');
81667
81668             var introModal = modalSelection.select('.content');
81669
81670             introModal
81671               .append('div')
81672               .attr('class', 'modal-section')
81673               .append('h3')
81674               .text(_t('restore.heading'));
81675
81676             introModal
81677               .append('div')
81678               .attr('class','modal-section')
81679               .append('p')
81680               .text(_t('restore.description'));
81681
81682             var buttonWrap = introModal
81683               .append('div')
81684               .attr('class', 'modal-actions');
81685
81686             var restore = buttonWrap
81687               .append('button')
81688               .attr('class', 'restore')
81689               .on('click', function () {
81690                 context.history().restore();
81691                 modalSelection.remove();
81692               });
81693
81694             restore
81695               .append('svg')
81696               .attr('class', 'logo logo-restore')
81697               .append('use')
81698               .attr('xlink:href', '#iD-logo-restore');
81699
81700             restore
81701               .append('div')
81702               .text(_t('restore.restore'));
81703
81704             var reset = buttonWrap
81705               .append('button')
81706               .attr('class', 'reset')
81707               .on('click', function () {
81708                 context.history().clearSaved();
81709                 modalSelection.remove();
81710               });
81711
81712             reset
81713               .append('svg')
81714               .attr('class', 'logo logo-reset')
81715               .append('use')
81716               .attr('xlink:href', '#iD-logo-reset');
81717
81718             reset
81719               .append('div')
81720               .text(_t('restore.reset'));
81721
81722             restore.node().focus();
81723           };
81724         }
81725
81726         function uiScale(context) {
81727             var projection = context.projection,
81728                 isImperial = !_mainLocalizer.usesMetric(),
81729                 maxLength = 180,
81730                 tickHeight = 8;
81731
81732
81733             function scaleDefs(loc1, loc2) {
81734                 var lat = (loc2[1] + loc1[1]) / 2,
81735                     conversion = (isImperial ? 3.28084 : 1),
81736                     dist = geoLonToMeters(loc2[0] - loc1[0], lat) * conversion,
81737                     scale = { dist: 0, px: 0, text: '' },
81738                     buckets, i, val, dLon;
81739
81740                 if (isImperial) {
81741                     buckets = [5280000, 528000, 52800, 5280, 500, 50, 5, 1];
81742                 } else {
81743                     buckets = [5000000, 500000, 50000, 5000, 500, 50, 5, 1];
81744                 }
81745
81746                 // determine a user-friendly endpoint for the scale
81747                 for (i = 0; i < buckets.length; i++) {
81748                     val = buckets[i];
81749                     if (dist >= val) {
81750                         scale.dist = Math.floor(dist / val) * val;
81751                         break;
81752                     } else {
81753                         scale.dist = +dist.toFixed(2);
81754                     }
81755                 }
81756
81757                 dLon = geoMetersToLon(scale.dist / conversion, lat);
81758                 scale.px = Math.round(projection([loc1[0] + dLon, loc1[1]])[0]);
81759
81760                 scale.text = displayLength(scale.dist / conversion, isImperial);
81761
81762                 return scale;
81763             }
81764
81765
81766             function update(selection) {
81767                 // choose loc1, loc2 along bottom of viewport (near where the scale will be drawn)
81768                 var dims = context.map().dimensions(),
81769                     loc1 = projection.invert([0, dims[1]]),
81770                     loc2 = projection.invert([maxLength, dims[1]]),
81771                     scale = scaleDefs(loc1, loc2);
81772
81773                 selection.select('.scale-path')
81774                     .attr('d', 'M0.5,0.5v' + tickHeight + 'h' + scale.px + 'v-' + tickHeight);
81775
81776                 selection.select('.scale-textgroup')
81777                     .attr('transform', 'translate(' + (scale.px + 8) + ',' + tickHeight + ')');
81778
81779                 selection.select('.scale-text')
81780                     .text(scale.text);
81781             }
81782
81783
81784             return function(selection) {
81785                 function switchUnits() {
81786                     isImperial = !isImperial;
81787                     selection.call(update);
81788                 }
81789
81790                 var scalegroup = selection.append('svg')
81791                     .attr('class', 'scale')
81792                     .on('click', switchUnits)
81793                     .append('g')
81794                     .attr('transform', 'translate(10,11)');
81795
81796                 scalegroup
81797                     .append('path')
81798                     .attr('class', 'scale-path');
81799
81800                 scalegroup
81801                     .append('g')
81802                     .attr('class', 'scale-textgroup')
81803                     .append('text')
81804                     .attr('class', 'scale-text');
81805
81806                 selection.call(update);
81807
81808                 context.map().on('move.scale', function() {
81809                     update(selection);
81810                 });
81811             };
81812         }
81813
81814         function uiShortcuts(context) {
81815             var detected = utilDetect();
81816             var _activeTab = 0;
81817             var _modalSelection;
81818             var _selection = select(null);
81819
81820
81821             context.keybinding()
81822                 .on([_t('shortcuts.toggle.key'), '?'], function () {
81823                     if (context.container().selectAll('.modal-shortcuts').size()) {  // already showing
81824                         if (_modalSelection) {
81825                             _modalSelection.close();
81826                             _modalSelection = null;
81827                         }
81828                     } else {
81829                         _modalSelection = uiModal(_selection);
81830                         _modalSelection.call(shortcutsModal);
81831                     }
81832                 });
81833
81834
81835             function shortcutsModal(_modalSelection) {
81836                 _modalSelection.select('.modal')
81837                     .classed('modal-shortcuts', true);
81838
81839                 var content = _modalSelection.select('.content');
81840
81841                 content
81842                     .append('div')
81843                     .attr('class', 'modal-section')
81844                     .append('h3')
81845                     .text(_t('shortcuts.title'));
81846
81847                 _mainFileFetcher.get('shortcuts')
81848                     .then(function(data) { content.call(render, data); })
81849                     .catch(function() { /* ignore */ });
81850             }
81851
81852
81853             function render(selection, dataShortcuts) {
81854                 var wrapper = selection
81855                     .selectAll('.wrapper')
81856                     .data([0]);
81857
81858                 var wrapperEnter = wrapper
81859                     .enter()
81860                     .append('div')
81861                     .attr('class', 'wrapper modal-section');
81862
81863                 var tabsBar = wrapperEnter
81864                     .append('div')
81865                     .attr('class', 'tabs-bar');
81866
81867                 var shortcutsList = wrapperEnter
81868                     .append('div')
81869                     .attr('class', 'shortcuts-list');
81870
81871                 wrapper = wrapper.merge(wrapperEnter);
81872
81873                 var tabs = tabsBar
81874                     .selectAll('.tab')
81875                     .data(dataShortcuts);
81876
81877                 var tabsEnter = tabs
81878                     .enter()
81879                     .append('div')
81880                     .attr('class', 'tab')
81881                     .on('click', function (d, i) {
81882                         _activeTab = i;
81883                         render(selection, dataShortcuts);
81884                     });
81885
81886                 tabsEnter
81887                     .append('span')
81888                     .text(function (d) { return _t(d.text); });
81889
81890                 tabs = tabs
81891                     .merge(tabsEnter);
81892
81893                 // Update
81894                 wrapper.selectAll('.tab')
81895                     .classed('active', function (d, i) {
81896                         return i === _activeTab;
81897                     });
81898
81899
81900                 var shortcuts = shortcutsList
81901                     .selectAll('.shortcut-tab')
81902                     .data(dataShortcuts);
81903
81904                 var shortcutsEnter = shortcuts
81905                     .enter()
81906                     .append('div')
81907                     .attr('class', function(d) { return 'shortcut-tab shortcut-tab-' + d.tab; });
81908
81909                 var columnsEnter = shortcutsEnter
81910                     .selectAll('.shortcut-column')
81911                     .data(function (d) { return d.columns; })
81912                     .enter()
81913                     .append('table')
81914                     .attr('class', 'shortcut-column');
81915
81916                 var rowsEnter = columnsEnter
81917                     .selectAll('.shortcut-row')
81918                     .data(function (d) { return d.rows; })
81919                     .enter()
81920                     .append('tr')
81921                     .attr('class', 'shortcut-row');
81922
81923
81924                 var sectionRows = rowsEnter
81925                     .filter(function (d) { return !d.shortcuts; });
81926
81927                 sectionRows
81928                     .append('td');
81929
81930                 sectionRows
81931                     .append('td')
81932                     .attr('class', 'shortcut-section')
81933                     .append('h3')
81934                     .text(function (d) { return _t(d.text); });
81935
81936
81937                 var shortcutRows = rowsEnter
81938                     .filter(function (d) { return d.shortcuts; });
81939
81940                 var shortcutKeys = shortcutRows
81941                     .append('td')
81942                     .attr('class', 'shortcut-keys');
81943
81944                 var modifierKeys = shortcutKeys
81945                     .filter(function (d) { return d.modifiers; });
81946
81947                 modifierKeys
81948                     .selectAll('kbd.modifier')
81949                     .data(function (d) {
81950                         if (detected.os === 'win' && d.text === 'shortcuts.editing.commands.redo') {
81951                             return ['⌘'];
81952                         } else if (detected.os !== 'mac' && d.text === 'shortcuts.browsing.display_options.fullscreen') {
81953                             return [];
81954                         } else {
81955                             return d.modifiers;
81956                         }
81957                     })
81958                     .enter()
81959                     .each(function () {
81960                         var selection = select(this);
81961
81962                         selection
81963                             .append('kbd')
81964                             .attr('class', 'modifier')
81965                             .text(function (d) { return uiCmd.display(d); });
81966
81967                         selection
81968                             .append('span')
81969                             .text('+');
81970                     });
81971
81972
81973                 shortcutKeys
81974                     .selectAll('kbd.shortcut')
81975                     .data(function (d) {
81976                         var arr = d.shortcuts;
81977                         if (detected.os === 'win' && d.text === 'shortcuts.editing.commands.redo') {
81978                             arr = ['Y'];
81979                         } else if (detected.os !== 'mac' && d.text === 'shortcuts.browsing.display_options.fullscreen') {
81980                             arr = ['F11'];
81981                         }
81982
81983                         // replace translations
81984                         arr = arr.map(function(s) {
81985                             return uiCmd.display(s.indexOf('.') !== -1 ? _t(s) : s);
81986                         });
81987
81988                         return utilArrayUniq(arr).map(function(s) {
81989                             return {
81990                                 shortcut: s,
81991                                 separator: d.separator,
81992                                 suffix: d.suffix
81993                             };
81994                         });
81995                     })
81996                     .enter()
81997                     .each(function (d, i, nodes) {
81998                         var selection = select(this);
81999                         var click = d.shortcut.toLowerCase().match(/(.*).click/);
82000
82001                         if (click && click[1]) {   // replace "left_click", "right_click" with mouse icon
82002                             selection
82003                                 .call(svgIcon('#iD-walkthrough-mouse-' + click[1], 'operation'));
82004                         } else if (d.shortcut.toLowerCase() === 'long-press') {
82005                             selection
82006                                 .call(svgIcon('#iD-walkthrough-longpress', 'longpress operation'));
82007                         } else if (d.shortcut.toLowerCase() === 'tap') {
82008                             selection
82009                                 .call(svgIcon('#iD-walkthrough-tap', 'tap operation'));
82010                         } else {
82011                             selection
82012                                 .append('kbd')
82013                                 .attr('class', 'shortcut')
82014                                 .text(function (d) { return d.shortcut; });
82015                         }
82016
82017                         if (i < nodes.length - 1) {
82018                             selection
82019                                 .append('span')
82020                                 .text(d.separator || '\u00a0' + _t('shortcuts.or') + '\u00a0');
82021                         } else if (i === nodes.length - 1 && d.suffix) {
82022                             selection
82023                                 .append('span')
82024                                 .text(d.suffix);
82025                         }
82026                     });
82027
82028
82029                 shortcutKeys
82030                     .filter(function(d) { return d.gesture; })
82031                     .each(function () {
82032                         var selection = select(this);
82033
82034                         selection
82035                             .append('span')
82036                             .text('+');
82037
82038                         selection
82039                             .append('span')
82040                             .attr('class', 'gesture')
82041                             .text(function (d) { return _t(d.gesture); });
82042                     });
82043
82044
82045                 shortcutRows
82046                     .append('td')
82047                     .attr('class', 'shortcut-desc')
82048                     .text(function (d) { return d.text ? _t(d.text) : '\u00a0'; });
82049
82050
82051                 shortcuts = shortcuts
82052                     .merge(shortcutsEnter);
82053
82054                 // Update
82055                 wrapper.selectAll('.shortcut-tab')
82056                     .style('display', function (d, i) {
82057                         return i === _activeTab ? 'flex' : 'none';
82058                     });
82059             }
82060
82061
82062             return function(selection, show) {
82063                 _selection = selection;
82064                 if (show) {
82065                     _modalSelection = uiModal(selection);
82066                     _modalSelection.call(shortcutsModal);
82067                 }
82068             };
82069         }
82070
82071         var pair_1 = pair;
82072
82073
82074         function search(input, dims) {
82075           if (!dims) { dims = 'NSEW'; }
82076           if (typeof input !== 'string') { return null; }
82077
82078           input = input.toUpperCase();
82079           var regex = /^[\s\,]*([NSEW])?\s*([\-|\—|\―]?[0-9.]+)[°º˚]?\s*(?:([0-9.]+)['’′‘]\s*)?(?:([0-9.]+)(?:''|"|”|″)\s*)?([NSEW])?/;
82080
82081           var m = input.match(regex);
82082           if (!m) { return null; }  // no match
82083
82084           var matched = m[0];
82085
82086           // extract dimension.. m[1] = leading, m[5] = trailing
82087           var dim;
82088           if (m[1] && m[5]) {                 // if matched both..
82089             dim = m[1];                       // keep leading
82090             matched = matched.slice(0, -1);   // remove trailing dimension from match
82091           } else {
82092             dim = m[1] || m[5];
82093           }
82094
82095           // if unrecognized dimension
82096           if (dim && dims.indexOf(dim) === -1) { return null; }
82097
82098           // extract DMS
82099           var deg = m[2] ? parseFloat(m[2]) : 0;
82100           var min = m[3] ? parseFloat(m[3]) / 60 : 0;
82101           var sec = m[4] ? parseFloat(m[4]) / 3600 : 0;
82102           var sign = (deg < 0) ? -1 : 1;
82103           if (dim === 'S' || dim === 'W') { sign *= -1; }
82104
82105           return {
82106             val: (Math.abs(deg) + min + sec) * sign,
82107             dim: dim,
82108             matched: matched,
82109             remain: input.slice(matched.length)
82110           };
82111         }
82112
82113
82114         function pair(input, dims) {
82115           input = input.trim();
82116           var one = search(input, dims);
82117           if (!one) { return null; }
82118
82119           input = one.remain.trim();
82120           var two = search(input, dims);
82121           if (!two || two.remain) { return null; }
82122
82123           if (one.dim) {
82124             return swapdim(one.val, two.val, one.dim);
82125           } else {
82126             return [one.val, two.val];
82127           }
82128         }
82129
82130
82131         function swapdim(a, b, dim) {
82132           if (dim === 'N' || dim === 'S') { return [a, b]; }
82133           if (dim === 'W' || dim === 'E') { return [b, a]; }
82134         }
82135
82136         function uiFeatureList(context) {
82137             var _geocodeResults;
82138
82139
82140             function featureList(selection) {
82141                 var header = selection
82142                     .append('div')
82143                     .attr('class', 'header fillL cf');
82144
82145                 header
82146                     .append('h3')
82147                     .text(_t('inspector.feature_list'));
82148
82149                 var searchWrap = selection
82150                     .append('div')
82151                     .attr('class', 'search-header');
82152
82153                 var search = searchWrap
82154                     .append('input')
82155                     .attr('placeholder', _t('inspector.search'))
82156                     .attr('type', 'search')
82157                     .call(utilNoAuto)
82158                     .on('keypress', keypress)
82159                     .on('keydown', keydown)
82160                     .on('input', inputevent);
82161
82162                 searchWrap
82163                     .call(svgIcon('#iD-icon-search', 'pre-text'));
82164
82165                 var listWrap = selection
82166                     .append('div')
82167                     .attr('class', 'inspector-body');
82168
82169                 var list = listWrap
82170                     .append('div')
82171                     .attr('class', 'feature-list cf');
82172
82173                 context
82174                     .on('exit.feature-list', clearSearch);
82175                 context.map()
82176                     .on('drawn.feature-list', mapDrawn);
82177
82178                 context.keybinding()
82179                     .on(uiCmd('⌘F'), focusSearch);
82180
82181
82182                 function focusSearch() {
82183                     var mode = context.mode() && context.mode().id;
82184                     if (mode !== 'browse') { return; }
82185
82186                     event.preventDefault();
82187                     search.node().focus();
82188                 }
82189
82190
82191                 function keydown() {
82192                     if (event.keyCode === 27) {  // escape
82193                         search.node().blur();
82194                     }
82195                 }
82196
82197
82198                 function keypress() {
82199                     var q = search.property('value'),
82200                         items = list.selectAll('.feature-list-item');
82201                     if (event.keyCode === 13 && q.length && items.size()) {  // return
82202                         click(items.datum());
82203                     }
82204                 }
82205
82206
82207                 function inputevent() {
82208                     _geocodeResults = undefined;
82209                     drawList();
82210                 }
82211
82212
82213                 function clearSearch() {
82214                     search.property('value', '');
82215                     drawList();
82216                 }
82217
82218
82219                 function mapDrawn(e) {
82220                     if (e.full) {
82221                         drawList();
82222                     }
82223                 }
82224
82225
82226                 function features() {
82227                     var result = [];
82228                     var graph = context.graph();
82229                     var visibleCenter = context.map().extent().center();
82230                     var q = search.property('value').toLowerCase();
82231
82232                     if (!q) { return result; }
82233
82234                     var idMatch = q.match(/(?:^|\W)(node|way|relation|[nwr])\W?0*([1-9]\d*)(?:\W|$)/i);
82235
82236                     if (idMatch) {
82237                         var elemType = idMatch[1].charAt(0);
82238                         var elemId = idMatch[2];
82239                         result.push({
82240                             id: elemType + elemId,
82241                             geometry: elemType === 'n' ? 'point' : elemType === 'w' ? 'line' : 'relation',
82242                             type: elemType === 'n' ? _t('inspector.node') : elemType === 'w' ? _t('inspector.way') : _t('inspector.relation'),
82243                             name: elemId
82244                         });
82245                     }
82246
82247                     var locationMatch = pair_1(q.toUpperCase()) || q.match(/^(-?\d+\.?\d*)\s+(-?\d+\.?\d*)$/);
82248
82249                     if (locationMatch) {
82250                         var loc = [parseFloat(locationMatch[0]), parseFloat(locationMatch[1])];
82251                         result.push({
82252                             id: -1,
82253                             geometry: 'point',
82254                             type: _t('inspector.location'),
82255                             name: dmsCoordinatePair([loc[1], loc[0]]),
82256                             location: loc
82257                         });
82258                     }
82259
82260                     var allEntities = graph.entities;
82261                     var localResults = [];
82262                     for (var id in allEntities) {
82263                         var entity = allEntities[id];
82264                         if (!entity) { continue; }
82265
82266                         var name = utilDisplayName(entity) || '';
82267                         if (name.toLowerCase().indexOf(q) < 0) { continue; }
82268
82269                         var matched = _mainPresetIndex.match(entity, graph);
82270                         var type = (matched && matched.name()) || utilDisplayType(entity.id);
82271                         var extent = entity.extent(graph);
82272                         var distance = extent ? geoSphericalDistance(visibleCenter, extent.center()) : 0;
82273
82274                         localResults.push({
82275                             id: entity.id,
82276                             entity: entity,
82277                             geometry: entity.geometry(graph),
82278                             type: type,
82279                             name: name,
82280                             distance: distance
82281                         });
82282
82283                         if (localResults.length > 100) { break; }
82284                     }
82285                     localResults = localResults.sort(function byDistance(a, b) {
82286                         return a.distance - b.distance;
82287                     });
82288                     result = result.concat(localResults);
82289
82290                     (_geocodeResults || []).forEach(function(d) {
82291                         if (d.osm_type && d.osm_id) {    // some results may be missing these - #1890
82292
82293                             // Make a temporary osmEntity so we can preset match
82294                             // and better localize the search result - #4725
82295                             var id = osmEntity.id.fromOSM(d.osm_type, d.osm_id);
82296                             var tags = {};
82297                             tags[d.class] = d.type;
82298
82299                             var attrs = { id: id, type: d.osm_type, tags: tags };
82300                             if (d.osm_type === 'way') {   // for ways, add some fake closed nodes
82301                                 attrs.nodes = ['a','a'];  // so that geometry area is possible
82302                             }
82303
82304                             var tempEntity = osmEntity(attrs);
82305                             var tempGraph = coreGraph([tempEntity]);
82306                             var matched = _mainPresetIndex.match(tempEntity, tempGraph);
82307                             var type = (matched && matched.name()) || utilDisplayType(id);
82308
82309                             result.push({
82310                                 id: tempEntity.id,
82311                                 geometry: tempEntity.geometry(tempGraph),
82312                                 type: type,
82313                                 name: d.display_name,
82314                                 extent: new geoExtent(
82315                                     [parseFloat(d.boundingbox[3]), parseFloat(d.boundingbox[0])],
82316                                     [parseFloat(d.boundingbox[2]), parseFloat(d.boundingbox[1])])
82317                             });
82318                         }
82319                     });
82320
82321                     if (q.match(/^[0-9]+$/)) {
82322                         // if query is just a number, possibly an OSM ID without a prefix
82323                         result.push({
82324                             id: 'n' + q,
82325                             geometry: 'point',
82326                             type: _t('inspector.node'),
82327                             name: q
82328                         });
82329                         result.push({
82330                             id: 'w' + q,
82331                             geometry: 'line',
82332                             type: _t('inspector.way'),
82333                             name: q
82334                         });
82335                         result.push({
82336                             id: 'r' + q,
82337                             geometry: 'relation',
82338                             type: _t('inspector.relation'),
82339                             name: q
82340                         });
82341                     }
82342
82343                     return result;
82344                 }
82345
82346
82347                 function drawList() {
82348                     var value = search.property('value');
82349                     var results = features();
82350
82351                     list.classed('filtered', value.length);
82352
82353                     var resultsIndicator = list.selectAll('.no-results-item')
82354                         .data([0])
82355                         .enter()
82356                         .append('button')
82357                         .property('disabled', true)
82358                         .attr('class', 'no-results-item')
82359                         .call(svgIcon('#iD-icon-alert', 'pre-text'));
82360
82361                     resultsIndicator.append('span')
82362                         .attr('class', 'entity-name');
82363
82364                     list.selectAll('.no-results-item .entity-name')
82365                         .text(_t('geocoder.no_results_worldwide'));
82366
82367                     if (services.geocoder) {
82368                       list.selectAll('.geocode-item')
82369                           .data([0])
82370                           .enter()
82371                           .append('button')
82372                           .attr('class', 'geocode-item')
82373                           .on('click', geocoderSearch)
82374                           .append('div')
82375                           .attr('class', 'label')
82376                           .append('span')
82377                           .attr('class', 'entity-name')
82378                           .text(_t('geocoder.search'));
82379                     }
82380
82381                     list.selectAll('.no-results-item')
82382                         .style('display', (value.length && !results.length) ? 'block' : 'none');
82383
82384                     list.selectAll('.geocode-item')
82385                         .style('display', (value && _geocodeResults === undefined) ? 'block' : 'none');
82386
82387                     list.selectAll('.feature-list-item')
82388                         .data([-1])
82389                         .remove();
82390
82391                     var items = list.selectAll('.feature-list-item')
82392                         .data(results, function(d) { return d.id; });
82393
82394                     var enter = items.enter()
82395                         .insert('button', '.geocode-item')
82396                         .attr('class', 'feature-list-item')
82397                         .on('mouseover', mouseover)
82398                         .on('mouseout', mouseout)
82399                         .on('click', click);
82400
82401                     var label = enter
82402                         .append('div')
82403                         .attr('class', 'label');
82404
82405                     label
82406                         .each(function(d) {
82407                             select(this)
82408                                 .call(svgIcon('#iD-icon-' + d.geometry, 'pre-text'));
82409                         });
82410
82411                     label
82412                         .append('span')
82413                         .attr('class', 'entity-type')
82414                         .text(function(d) { return d.type; });
82415
82416                     label
82417                         .append('span')
82418                         .attr('class', 'entity-name')
82419                         .text(function(d) { return d.name; });
82420
82421                     enter
82422                         .style('opacity', 0)
82423                         .transition()
82424                         .style('opacity', 1);
82425
82426                     items.order();
82427
82428                     items.exit()
82429                         .remove();
82430                 }
82431
82432
82433                 function mouseover(d) {
82434                     if (d.id === -1) { return; }
82435
82436                     utilHighlightEntities([d.id], true, context);
82437                 }
82438
82439
82440                 function mouseout(d) {
82441                     if (d.id === -1) { return; }
82442
82443                     utilHighlightEntities([d.id], false, context);
82444                 }
82445
82446
82447                 function click(d) {
82448                     event.preventDefault();
82449
82450                     if (d.location) {
82451                         context.map().centerZoomEase([d.location[1], d.location[0]], 19);
82452
82453                     } else if (d.entity) {
82454                         utilHighlightEntities([d.id], false, context);
82455
82456                         context.enter(modeSelect(context, [d.entity.id]));
82457                         context.map().zoomToEase(d.entity);
82458
82459                     } else {
82460                         // download, zoom to, and select the entity with the given ID
82461                         context.zoomToEntity(d.id);
82462                     }
82463                 }
82464
82465
82466                 function geocoderSearch() {
82467                     services.geocoder.search(search.property('value'), function (err, resp) {
82468                         _geocodeResults = resp || [];
82469                         drawList();
82470                     });
82471                 }
82472             }
82473
82474
82475             return featureList;
82476         }
82477
82478         function uiSectionEntityIssues(context) {
82479
82480             var _entityIDs = [];
82481             var _issues = [];
82482             var _activeIssueID;
82483
82484             var section = uiSection('entity-issues', context)
82485                 .shouldDisplay(function() {
82486                     return _issues.length > 0;
82487                 })
82488                 .title(function() {
82489                     return _t('issues.list_title', { count: _issues.length });
82490                 })
82491                 .disclosureContent(renderDisclosureContent);
82492
82493             context.validator()
82494                 .on('validated.entity_issues', function() {
82495                     // Refresh on validated events
82496                     reloadIssues();
82497                     section.reRender();
82498                 })
82499                 .on('focusedIssue.entity_issues', function(issue) {
82500                      makeActiveIssue(issue.id);
82501                 });
82502
82503             function reloadIssues() {
82504                 _issues = context.validator().getSharedEntityIssues(_entityIDs, { includeDisabledRules: true });
82505             }
82506
82507             function makeActiveIssue(issueID) {
82508                 _activeIssueID = issueID;
82509                 section.selection().selectAll('.issue-container')
82510                     .classed('active', function(d) { return d.id === _activeIssueID; });
82511             }
82512
82513             function renderDisclosureContent(selection) {
82514
82515                 selection.classed('grouped-items-area', true);
82516
82517                 _activeIssueID = _issues.length > 0 ? _issues[0].id : null;
82518
82519                 var containers = selection.selectAll('.issue-container')
82520                     .data(_issues, function(d) { return d.id; });
82521
82522                 // Exit
82523                 containers.exit()
82524                     .remove();
82525
82526                 // Enter
82527                 var containersEnter = containers.enter()
82528                     .append('div')
82529                     .attr('class', 'issue-container');
82530
82531
82532                 var itemsEnter = containersEnter
82533                     .append('div')
82534                     .attr('class', function(d) { return 'issue severity-' + d.severity; })
82535                     .on('mouseover.highlight', function(d) {
82536                         // don't hover-highlight the selected entity
82537                         var ids = d.entityIds
82538                             .filter(function(e) { return _entityIDs.indexOf(e) === -1; });
82539
82540                         utilHighlightEntities(ids, true, context);
82541                     })
82542                     .on('mouseout.highlight', function(d) {
82543                         var ids = d.entityIds
82544                             .filter(function(e) { return _entityIDs.indexOf(e) === -1; });
82545
82546                         utilHighlightEntities(ids, false, context);
82547                     });
82548
82549                 var labelsEnter = itemsEnter
82550                     .append('div')
82551                     .attr('class', 'issue-label')
82552                     .on('click', function(d) {
82553
82554                         makeActiveIssue(d.id); // expand only the clicked item
82555
82556                         var extent = d.extent(context.graph());
82557                         if (extent) {
82558                             var setZoom = Math.max(context.map().zoom(), 19);
82559                             context.map().unobscuredCenterZoomEase(extent.center(), setZoom);
82560                         }
82561                     });
82562
82563                 var textEnter = labelsEnter
82564                     .append('span')
82565                     .attr('class', 'issue-text');
82566
82567                 textEnter
82568                     .append('span')
82569                     .attr('class', 'issue-icon')
82570                     .each(function(d) {
82571                         var iconName = '#iD-icon-' + (d.severity === 'warning' ? 'alert' : 'error');
82572                         select(this)
82573                             .call(svgIcon(iconName));
82574                     });
82575
82576                 textEnter
82577                     .append('span')
82578                     .attr('class', 'issue-message');
82579
82580
82581                 var infoButton = labelsEnter
82582                     .append('button')
82583                     .attr('class', 'issue-info-button')
82584                     .attr('title', _t('icons.information'))
82585                     .attr('tabindex', -1)
82586                     .call(svgIcon('#iD-icon-inspect'));
82587
82588                 infoButton
82589                     .on('click', function () {
82590                         event.stopPropagation();
82591                         event.preventDefault();
82592                         this.blur();    // avoid keeping focus on the button - #4641
82593
82594                         var container = select(this.parentNode.parentNode.parentNode);
82595                         var info = container.selectAll('.issue-info');
82596                         var isExpanded = info.classed('expanded');
82597
82598                         if (isExpanded) {
82599                             info
82600                                 .transition()
82601                                 .duration(200)
82602                                 .style('max-height', '0px')
82603                                 .style('opacity', '0')
82604                                 .on('end', function () {
82605                                     info.classed('expanded', false);
82606                                 });
82607                         } else {
82608                             info
82609                                 .classed('expanded', true)
82610                                 .transition()
82611                                 .duration(200)
82612                                 .style('max-height', '200px')
82613                                 .style('opacity', '1')
82614                                 .on('end', function () {
82615                                     info.style('max-height', null);
82616                                 });
82617                         }
82618                     });
82619
82620                 itemsEnter
82621                     .append('ul')
82622                     .attr('class', 'issue-fix-list');
82623
82624                 containersEnter
82625                     .append('div')
82626                     .attr('class', 'issue-info')
82627                     .style('max-height', '0')
82628                     .style('opacity', '0')
82629                     .each(function(d) {
82630                         if (typeof d.reference === 'function') {
82631                             select(this)
82632                                 .call(d.reference);
82633                         } else {
82634                             select(this)
82635                                 .text(_t('inspector.no_documentation_key'));
82636                         }
82637                     });
82638
82639
82640                 // Update
82641                 containers = containers
82642                     .merge(containersEnter)
82643                     .classed('active', function(d) { return d.id === _activeIssueID; });
82644
82645                 containers.selectAll('.issue-message')
82646                     .text(function(d) {
82647                         return d.message(context);
82648                     });
82649
82650                 // fixes
82651                 var fixLists = containers.selectAll('.issue-fix-list');
82652
82653                 var fixes = fixLists.selectAll('.issue-fix-item')
82654                     .data(function(d) { return d.fixes ? d.fixes(context) : []; }, function(fix) { return fix.id; });
82655
82656                 fixes.exit()
82657                     .remove();
82658
82659                 var fixesEnter = fixes.enter()
82660                     .append('li')
82661                     .attr('class', 'issue-fix-item')
82662                     .on('click', function(d) {
82663                         // not all fixes are actionable
82664                         if (!select(this).classed('actionable') || !d.onClick) { return; }
82665
82666                         // Don't run another fix for this issue within a second of running one
82667                         // (Necessary for "Select a feature type" fix. Most fixes should only ever run once)
82668                         if (d.issue.dateLastRanFix && new Date() - d.issue.dateLastRanFix < 1000) { return; }
82669                         d.issue.dateLastRanFix = new Date();
82670
82671                         // remove hover-highlighting
82672                         utilHighlightEntities(d.issue.entityIds.concat(d.entityIds), false, context);
82673
82674                         new Promise(function(resolve, reject) {
82675                             d.onClick(context, resolve, reject);
82676                             if (d.onClick.length <= 1) {
82677                                 // if the fix doesn't take any completion parameters then consider it resolved
82678                                 resolve();
82679                             }
82680                         })
82681                         .then(function() {
82682                             // revalidate whenever the fix has finished running successfully
82683                             context.validator().validate();
82684                         });
82685                     })
82686                     .on('mouseover.highlight', function(d) {
82687                         utilHighlightEntities(d.entityIds, true, context);
82688                     })
82689                     .on('mouseout.highlight', function(d) {
82690                         utilHighlightEntities(d.entityIds, false, context);
82691                     });
82692
82693                 fixesEnter
82694                     .append('span')
82695                     .attr('class', 'fix-icon')
82696                     .each(function(d) {
82697                         var iconName = d.icon || 'iD-icon-wrench';
82698                         if (iconName.startsWith('maki')) {
82699                             iconName += '-15';
82700                         }
82701                         select(this).call(svgIcon('#' + iconName));
82702                     });
82703
82704                 fixesEnter
82705                     .append('span')
82706                     .attr('class', 'fix-message')
82707                     .text(function(d) { return d.title; });
82708
82709                 fixesEnter.merge(fixes)
82710                     .classed('actionable', function(d) {
82711                         return d.onClick;
82712                     })
82713                     .attr('title', function(d) {
82714                         if (d.disabledReason) {
82715                             return d.disabledReason;
82716                         }
82717                         return null;
82718                     });
82719             }
82720
82721             section.entityIDs = function(val) {
82722                 if (!arguments.length) { return _entityIDs; }
82723                 if (!_entityIDs || !val || !utilArrayIdentical(_entityIDs, val)) {
82724                     _entityIDs = val;
82725                     _activeIssueID = null;
82726                     reloadIssues();
82727                 }
82728                 return section;
82729             };
82730
82731             return section;
82732         }
82733
82734         function uiPresetIcon() {
82735           var _preset;
82736           var _geometry;
82737           var _sizeClass = 'medium';
82738
82739
82740           function isSmall() {
82741             return _sizeClass === 'small';
82742           }
82743
82744
82745           function presetIcon(selection) {
82746             selection.each(render);
82747           }
82748
82749
82750           function getIcon(p, geom) {
82751             if (isSmall() && p.isFallback && p.isFallback())
82752               { return 'iD-icon-' + p.id; }
82753             else if (p.icon)
82754               { return p.icon; }
82755             else if (geom === 'line')
82756               { return 'iD-other-line'; }
82757             else if (geom === 'vertex')
82758               { return p.isFallback() ? '' : 'temaki-vertex'; }
82759             else if (isSmall() && geom === 'point')
82760               { return ''; }
82761             else
82762               { return 'maki-marker-stroked'; }
82763           }
82764
82765
82766           function renderPointBorder(enter) {
82767             var w = 40;
82768             var h = 40;
82769
82770             enter
82771               .append('svg')
82772               .attr('class', 'preset-icon-fill preset-icon-point-border')
82773               .attr('width', w)
82774               .attr('height', h)
82775               .attr('viewBox', ("0 0 " + w + " " + h))
82776               .append('path')
82777               .attr('transform', 'translate(11.5, 8)')
82778               .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');
82779           }
82780
82781
82782           function renderCircleFill(fillEnter) {
82783             var w = 60;
82784             var h = 60;
82785             var d = 40;
82786
82787             fillEnter
82788               .append('svg')
82789               .attr('class', 'preset-icon-fill preset-icon-fill-vertex')
82790               .attr('width', w)
82791               .attr('height', h)
82792               .attr('viewBox', ("0 0 " + w + " " + h))
82793               .append('circle')
82794               .attr('cx', w / 2)
82795               .attr('cy', h / 2)
82796               .attr('r', d / 2);
82797           }
82798
82799
82800           function renderSquareFill(fillEnter) {
82801             var d = isSmall() ? 40 : 60;
82802             var w = d;
82803             var h = d;
82804             var l = d * 2/3;
82805             var c1 = (w-l) / 2;
82806             var c2 = c1 + l;
82807
82808             fillEnter = fillEnter
82809               .append('svg')
82810               .attr('class', 'preset-icon-fill preset-icon-fill-area')
82811               .attr('width', w)
82812               .attr('height', h)
82813               .attr('viewBox', ("0 0 " + w + " " + h));
82814
82815             ['fill', 'stroke'].forEach(function (klass) {
82816               fillEnter
82817                 .append('path')
82818                 .attr('d', ("M" + c1 + " " + c1 + " L" + c1 + " " + c2 + " L" + c2 + " " + c2 + " L" + c2 + " " + c1 + " Z"))
82819                 .attr('class', ("line area " + klass));
82820             });
82821
82822             var rVertex = 2.5;
82823             [[c1, c1], [c1, c2], [c2, c2], [c2, c1]].forEach(function (point) {
82824               fillEnter
82825                 .append('circle')
82826                 .attr('class', 'vertex')
82827                 .attr('cx', point[0])
82828                 .attr('cy', point[1])
82829                 .attr('r', rVertex);
82830             });
82831
82832             if (!isSmall()) {
82833               var rMidpoint = 1.25;
82834               [[c1, w/2], [c2, w/2], [h/2, c1], [h/2, c2]].forEach(function (point) {
82835                 fillEnter
82836                   .append('circle')
82837                   .attr('class', 'midpoint')
82838                   .attr('cx', point[0])
82839                   .attr('cy', point[1])
82840                   .attr('r', rMidpoint);
82841               });
82842             }
82843           }
82844
82845
82846           function renderLine(lineEnter) {
82847             var d = isSmall() ? 40 : 60;
82848             // draw the line parametrically
82849             var w = d;
82850             var h = d;
82851             var y = Math.round(d * 0.72);
82852             var l = Math.round(d * 0.6);
82853             var r = 2.5;
82854             var x1 = (w - l) / 2;
82855             var x2 = x1 + l;
82856
82857             lineEnter = lineEnter
82858               .append('svg')
82859               .attr('class', 'preset-icon-line')
82860               .attr('width', w)
82861               .attr('height', h)
82862               .attr('viewBox', ("0 0 " + w + " " + h));
82863
82864             ['casing', 'stroke'].forEach(function (klass) {
82865               lineEnter
82866                 .append('path')
82867                 .attr('d', ("M" + x1 + " " + y + " L" + x2 + " " + y))
82868                 .attr('class', ("line " + klass));
82869             });
82870
82871             [[x1-1, y], [x2+1, y]].forEach(function (point) {
82872               lineEnter
82873                 .append('circle')
82874                 .attr('class', 'vertex')
82875                 .attr('cx', point[0])
82876                 .attr('cy', point[1])
82877                 .attr('r', r);
82878             });
82879           }
82880
82881
82882           function renderRoute(routeEnter) {
82883             var d = isSmall() ? 40 : 60;
82884             // draw the route parametrically
82885             var w = d;
82886             var h = d;
82887             var y1 = Math.round(d * 0.80);
82888             var y2 = Math.round(d * 0.68);
82889             var l = Math.round(d * 0.6);
82890             var r = 2;
82891             var x1 = (w - l) / 2;
82892             var x2 = x1 + l / 3;
82893             var x3 = x2 + l / 3;
82894             var x4 = x3 + l / 3;
82895
82896             routeEnter = routeEnter
82897               .append('svg')
82898               .attr('class', 'preset-icon-route')
82899               .attr('width', w)
82900               .attr('height', h)
82901               .attr('viewBox', ("0 0 " + w + " " + h));
82902
82903             ['casing', 'stroke'].forEach(function (klass) {
82904               routeEnter
82905                 .append('path')
82906                 .attr('d', ("M" + x1 + " " + y1 + " L" + x2 + " " + y2))
82907                 .attr('class', ("segment0 line " + klass));
82908               routeEnter
82909                 .append('path')
82910                 .attr('d', ("M" + x2 + " " + y2 + " L" + x3 + " " + y1))
82911                 .attr('class', ("segment1 line " + klass));
82912               routeEnter
82913                 .append('path')
82914                 .attr('d', ("M" + x3 + " " + y1 + " L" + x4 + " " + y2))
82915                 .attr('class', ("segment2 line " + klass));
82916             });
82917
82918             [[x1, y1], [x2, y2], [x3, y1], [x4, y2]].forEach(function (point) {
82919               routeEnter
82920                 .append('circle')
82921                 .attr('class', 'vertex')
82922                 .attr('cx', point[0])
82923                 .attr('cy', point[1])
82924                 .attr('r', r);
82925             });
82926           }
82927
82928
82929           // Route icons are drawn with a zigzag annotation underneath:
82930           //     o   o
82931           //    / \ /
82932           //   o   o
82933           // This dataset defines the styles that are used to draw the zigzag segments.
82934           var routeSegments = {
82935             bicycle: ['highway/cycleway', 'highway/cycleway', 'highway/cycleway'],
82936             bus: ['highway/unclassified', 'highway/secondary', 'highway/primary'],
82937             trolleybus: ['highway/unclassified', 'highway/secondary', 'highway/primary'],
82938             detour: ['highway/tertiary', 'highway/residential', 'highway/unclassified'],
82939             ferry: ['route/ferry', 'route/ferry', 'route/ferry'],
82940             foot: ['highway/footway', 'highway/footway', 'highway/footway'],
82941             hiking: ['highway/path', 'highway/path', 'highway/path'],
82942             horse: ['highway/bridleway', 'highway/bridleway', 'highway/bridleway'],
82943             light_rail: ['railway/light_rail', 'railway/light_rail', 'railway/light_rail'],
82944             monorail: ['railway/monorail', 'railway/monorail', 'railway/monorail'],
82945             pipeline: ['man_made/pipeline', 'man_made/pipeline', 'man_made/pipeline'],
82946             piste: ['piste/downhill', 'piste/hike', 'piste/nordic'],
82947             power: ['power/line', 'power/line', 'power/line'],
82948             road: ['highway/secondary', 'highway/primary', 'highway/trunk'],
82949             subway: ['railway/subway', 'railway/subway', 'railway/subway'],
82950             train: ['railway/rail', 'railway/rail', 'railway/rail'],
82951             tram: ['railway/tram', 'railway/tram', 'railway/tram'],
82952             waterway: ['waterway/stream', 'waterway/stream', 'waterway/stream']
82953           };
82954
82955
82956           function render() {
82957             var p = _preset.apply(this, arguments);
82958             var geom = _geometry ? _geometry.apply(this, arguments) : null;
82959             if (geom === 'relation' && p.tags && ((p.tags.type === 'route' && p.tags.route && routeSegments[p.tags.route]) || p.tags.type === 'waterway')) {
82960               geom = 'route';
82961             }
82962
82963             var showThirdPartyIcons = corePreferences('preferences.privacy.thirdpartyicons') || 'true';
82964             var isFallback = isSmall() && p.isFallback && p.isFallback();
82965             var imageURL = (showThirdPartyIcons === 'true') && p.imageURL;
82966             var picon = getIcon(p, geom);
82967             var isMaki = picon && /^maki-/.test(picon);
82968             var isTemaki = picon && /^temaki-/.test(picon);
82969             var isFa = picon && /^fa[srb]-/.test(picon);
82970             var isTnp = picon && /^tnp-/.test(picon);
82971             var isiDIcon = picon && !(isMaki || isTemaki || isFa || isTnp);
82972             var isCategory = !p.setTags;
82973             var drawPoint = picon && geom === 'point' && isSmall() && !isFallback;
82974             var drawVertex = picon !== null && geom === 'vertex' && (!isSmall() || !isFallback);
82975             var drawLine = picon && geom === 'line' && !isFallback && !isCategory;
82976             var drawArea = picon && geom === 'area' && !isFallback;
82977             var drawRoute = picon && geom === 'route';
82978             var isFramed = (drawVertex || drawArea || drawLine || drawRoute);
82979
82980             var tags = !isCategory ? p.setTags({}, geom) : {};
82981             for (var k in tags) {
82982               if (tags[k] === '*') {
82983                 tags[k] = 'yes';
82984               }
82985             }
82986
82987             var tagClasses = svgTagClasses().getClassesString(tags, '');
82988             var selection = select(this);
82989
82990             var container = selection.selectAll('.preset-icon-container')
82991               .data([0]);
82992
82993             container = container.enter()
82994               .append('div')
82995               .attr('class', ("preset-icon-container " + _sizeClass))
82996               .merge(container);
82997
82998             container
82999               .classed('showing-img', !!imageURL)
83000               .classed('fallback', isFallback);
83001
83002
83003             var pointBorder = container.selectAll('.preset-icon-point-border')
83004               .data(drawPoint ? [0] : []);
83005
83006             pointBorder.exit()
83007               .remove();
83008
83009             var pointBorderEnter = pointBorder.enter();
83010             renderPointBorder(pointBorderEnter);
83011             pointBorder = pointBorderEnter.merge(pointBorder);
83012
83013
83014             var vertexFill = container.selectAll('.preset-icon-fill-vertex')
83015               .data(drawVertex ? [0] : []);
83016
83017             vertexFill.exit()
83018               .remove();
83019
83020             var vertexFillEnter = vertexFill.enter();
83021             renderCircleFill(vertexFillEnter);
83022             vertexFill = vertexFillEnter.merge(vertexFill);
83023
83024
83025             var fill = container.selectAll('.preset-icon-fill-area')
83026               .data(drawArea ? [0] : []);
83027
83028             fill.exit()
83029               .remove();
83030
83031             var fillEnter = fill.enter();
83032             renderSquareFill(fillEnter);
83033             fill = fillEnter.merge(fill);
83034
83035             fill.selectAll('path.stroke')
83036               .attr('class', ("area stroke " + tagClasses));
83037             fill.selectAll('path.fill')
83038               .attr('class', ("area fill " + tagClasses));
83039
83040
83041             var line = container.selectAll('.preset-icon-line')
83042               .data(drawLine ? [0] : []);
83043
83044             line.exit()
83045               .remove();
83046
83047             var lineEnter = line.enter();
83048             renderLine(lineEnter);
83049             line = lineEnter.merge(line);
83050
83051             line.selectAll('path.stroke')
83052               .attr('class', ("line stroke " + tagClasses));
83053             line.selectAll('path.casing')
83054               .attr('class', ("line casing " + tagClasses));
83055
83056
83057             var route = container.selectAll('.preset-icon-route')
83058               .data(drawRoute ? [0] : []);
83059
83060             route.exit()
83061               .remove();
83062
83063             var routeEnter = route.enter();
83064             renderRoute(routeEnter);
83065             route = routeEnter.merge(route);
83066
83067             if (drawRoute) {
83068               var routeType = p.tags.type === 'waterway' ? 'waterway' : p.tags.route;
83069               var segmentPresetIDs = routeSegments[routeType];
83070               for (var i in segmentPresetIDs) {
83071                 var segmentPreset = _mainPresetIndex.item(segmentPresetIDs[i]);
83072                 var segmentTagClasses = svgTagClasses().getClassesString(segmentPreset.tags, '');
83073                 route.selectAll(("path.stroke.segment" + i))
83074                   .attr('class', ("segment" + i + " line stroke " + segmentTagClasses));
83075                 route.selectAll(("path.casing.segment" + i))
83076                   .attr('class', ("segment" + i + " line casing " + segmentTagClasses));
83077               }
83078             }
83079
83080
83081             var icon = container.selectAll('.preset-icon')
83082               .data(picon ? [0] : []);
83083
83084             icon.exit()
83085               .remove();
83086
83087             icon = icon.enter()
83088               .append('div')
83089               .attr('class', 'preset-icon')
83090               .call(svgIcon(''))
83091               .merge(icon);
83092
83093             icon
83094               .attr('class', 'preset-icon ' + (geom ? geom + '-geom' : ''))
83095               .classed('framed', isFramed)
83096               .classed('preset-icon-iD', isiDIcon);
83097
83098             icon.selectAll('svg')
83099               .attr('class', 'icon ' + picon + ' ' + (!isiDIcon && geom !== 'line'  ? '' : tagClasses));
83100
83101             icon.selectAll('use')
83102               .attr('href', '#' + picon + (isMaki ? (isSmall() && geom === 'point' ? '-11' : '-15') : ''));
83103
83104             var imageIcon = container.selectAll('img.image-icon')
83105               .data(imageURL ? [0] : []);
83106
83107             imageIcon.exit()
83108               .remove();
83109
83110             imageIcon = imageIcon.enter()
83111               .append('img')
83112               .attr('class', 'image-icon')
83113               .on('load', function () { return container.classed('showing-img', true); } )
83114               .on('error', function () { return container.classed('showing-img', false); } )
83115               .merge(imageIcon);
83116
83117             imageIcon
83118               .attr('src', imageURL);
83119           }
83120
83121
83122           presetIcon.preset = function(val) {
83123             if (!arguments.length) { return _preset; }
83124             _preset = utilFunctor(val);
83125             return presetIcon;
83126           };
83127
83128
83129           presetIcon.geometry = function(val) {
83130             if (!arguments.length) { return _geometry; }
83131             _geometry = utilFunctor(val);
83132             return presetIcon;
83133           };
83134
83135
83136           presetIcon.sizeClass = function(val) {
83137             if (!arguments.length) { return _sizeClass; }
83138             _sizeClass = val;
83139             return presetIcon;
83140           };
83141
83142           return presetIcon;
83143         }
83144
83145         function uiSectionFeatureType(context) {
83146
83147             var dispatch$1 = dispatch('choose');
83148
83149             var _entityIDs = [];
83150             var _presets = [];
83151
83152             var _tagReference;
83153
83154             var section = uiSection('feature-type', context)
83155                 .title(_t('inspector.feature_type'))
83156                 .disclosureContent(renderDisclosureContent);
83157
83158             function renderDisclosureContent(selection) {
83159
83160                 selection.classed('preset-list-item', true);
83161                 selection.classed('mixed-types', _presets.length > 1);
83162
83163                 var presetButtonWrap = selection
83164                     .selectAll('.preset-list-button-wrap')
83165                     .data([0])
83166                     .enter()
83167                     .append('div')
83168                     .attr('class', 'preset-list-button-wrap');
83169
83170                 var presetButton = presetButtonWrap
83171                     .append('button')
83172                     .attr('class', 'preset-list-button preset-reset')
83173                     .call(uiTooltip()
83174                         .title(_t('inspector.back_tooltip'))
83175                         .placement('bottom')
83176                     );
83177
83178                 presetButton.append('div')
83179                     .attr('class', 'preset-icon-container');
83180
83181                 presetButton
83182                     .append('div')
83183                     .attr('class', 'label')
83184                     .append('div')
83185                     .attr('class', 'label-inner');
83186
83187                 presetButtonWrap.append('div')
83188                     .attr('class', 'accessory-buttons');
83189
83190                 var tagReferenceBodyWrap = selection
83191                     .selectAll('.tag-reference-body-wrap')
83192                     .data([0]);
83193
83194                 tagReferenceBodyWrap = tagReferenceBodyWrap
83195                     .enter()
83196                     .append('div')
83197                     .attr('class', 'tag-reference-body-wrap')
83198                     .merge(tagReferenceBodyWrap);
83199
83200                 // update header
83201                 if (_tagReference) {
83202                     selection.selectAll('.preset-list-button-wrap .accessory-buttons')
83203                         .style('display', _presets.length === 1 ? null : 'none')
83204                         .call(_tagReference.button);
83205
83206                     tagReferenceBodyWrap
83207                         .style('display', _presets.length === 1 ? null : 'none')
83208                         .call(_tagReference.body);
83209                 }
83210
83211                 selection.selectAll('.preset-reset')
83212                     .on('click', function() {
83213                          dispatch$1.call('choose', this, _presets);
83214                     })
83215                     .on('pointerdown pointerup mousedown mouseup', function() {
83216                         event.preventDefault();
83217                         event.stopPropagation();
83218                     });
83219
83220                 var geometries = entityGeometries();
83221                 selection.select('.preset-list-item button')
83222                     .call(uiPresetIcon()
83223                         .geometry(_presets.length === 1 ? (geometries.length === 1 && geometries[0]) : null)
83224                         .preset(_presets.length === 1 ? _presets[0] : _mainPresetIndex.item('point'))
83225                     );
83226
83227                 // NOTE: split on en-dash, not a hypen (to avoid conflict with hyphenated names)
83228                 var names = _presets.length === 1 ? _presets[0].name().split(' – ') : [_t('inspector.multiple_types')];
83229
83230                 var label = selection.select('.label-inner');
83231                 var nameparts = label.selectAll('.namepart')
83232                     .data(names, function(d) { return d; });
83233
83234                 nameparts.exit()
83235                     .remove();
83236
83237                 nameparts
83238                     .enter()
83239                     .append('div')
83240                     .attr('class', 'namepart')
83241                     .text(function(d) { return d; });
83242             }
83243
83244             section.entityIDs = function(val) {
83245                 if (!arguments.length) { return _entityIDs; }
83246                 _entityIDs = val;
83247                 return section;
83248             };
83249
83250             section.presets = function(val) {
83251                 if (!arguments.length) { return _presets; }
83252
83253                 // don't reload the same preset
83254                 if (!utilArrayIdentical(val, _presets)) {
83255                     _presets = val;
83256
83257                     var geometries = entityGeometries();
83258                     if (_presets.length === 1 && geometries.length) {
83259                         _tagReference = uiTagReference(_presets[0].reference(geometries[0]))
83260                             .showing(false);
83261                     }
83262                 }
83263
83264                 return section;
83265             };
83266
83267             function entityGeometries() {
83268
83269                 var counts = {};
83270
83271                 for (var i in _entityIDs) {
83272                     var geometry = context.graph().geometry(_entityIDs[i]);
83273                     if (!counts[geometry]) { counts[geometry] = 0; }
83274                     counts[geometry] += 1;
83275                 }
83276
83277                 return Object.keys(counts).sort(function(geom1, geom2) {
83278                     return counts[geom2] - counts[geom1];
83279                 });
83280             }
83281
83282             return utilRebind(section, dispatch$1, 'on');
83283         }
83284
83285         // This currently only works with the 'restrictions' field
83286         // It borrows some code from uiHelp
83287
83288         function uiFieldHelp(context, fieldName) {
83289             var fieldHelp = {};
83290             var _inspector = select(null);
83291             var _wrap = select(null);
83292             var _body = select(null);
83293
83294             var fieldHelpKeys = {
83295                 restrictions: [
83296                     ['about',[
83297                         'about',
83298                         'from_via_to',
83299                         'maxdist',
83300                         'maxvia'
83301                     ]],
83302                     ['inspecting',[
83303                         'about',
83304                         'from_shadow',
83305                         'allow_shadow',
83306                         'restrict_shadow',
83307                         'only_shadow',
83308                         'restricted',
83309                         'only'
83310                     ]],
83311                     ['modifying',[
83312                         'about',
83313                         'indicators',
83314                         'allow_turn',
83315                         'restrict_turn',
83316                         'only_turn'
83317                     ]],
83318                     ['tips',[
83319                         'simple',
83320                         'simple_example',
83321                         'indirect',
83322                         'indirect_example',
83323                         'indirect_noedit'
83324                     ]]
83325                 ]
83326             };
83327
83328             var fieldHelpHeadings = {};
83329
83330             var replacements = {
83331                 distField: _t('restriction.controls.distance'),
83332                 viaField: _t('restriction.controls.via'),
83333                 fromShadow: icon('#iD-turn-shadow', 'pre-text shadow from'),
83334                 allowShadow: icon('#iD-turn-shadow', 'pre-text shadow allow'),
83335                 restrictShadow: icon('#iD-turn-shadow', 'pre-text shadow restrict'),
83336                 onlyShadow: icon('#iD-turn-shadow', 'pre-text shadow only'),
83337                 allowTurn: icon('#iD-turn-yes', 'pre-text turn'),
83338                 restrictTurn: icon('#iD-turn-no', 'pre-text turn'),
83339                 onlyTurn: icon('#iD-turn-only', 'pre-text turn')
83340             };
83341
83342
83343             // For each section, squash all the texts into a single markdown document
83344             var docs = fieldHelpKeys[fieldName].map(function(key) {
83345                 var helpkey = 'help.field.' + fieldName + '.' + key[0];
83346                 var text = key[1].reduce(function(all, part) {
83347                     var subkey = helpkey + '.' + part;
83348                     var depth = fieldHelpHeadings[subkey];                     // is this subkey a heading?
83349                     var hhh = depth ? Array(depth + 1).join('#') + ' ' : '';   // if so, prepend with some ##'s
83350                     return all + hhh + _t(subkey, replacements) + '\n\n';
83351                 }, '');
83352
83353                 return {
83354                     key: helpkey,
83355                     title: _t(helpkey + '.title'),
83356                     html: marked_1(text.trim())
83357                 };
83358             });
83359
83360
83361             function show() {
83362                 updatePosition();
83363
83364                 _body
83365                     .classed('hide', false)
83366                     .style('opacity', '0')
83367                     .transition()
83368                     .duration(200)
83369                     .style('opacity', '1');
83370             }
83371
83372
83373             function hide() {
83374                 _body
83375                     .classed('hide', true)
83376                     .transition()
83377                     .duration(200)
83378                     .style('opacity', '0')
83379                     .on('end', function () {
83380                         _body.classed('hide', true);
83381                     });
83382             }
83383
83384
83385             function clickHelp(index) {
83386                 var d = docs[index];
83387                 var tkeys = fieldHelpKeys[fieldName][index][1];
83388
83389                 _body.selectAll('.field-help-nav-item')
83390                     .classed('active', function(d, i) { return i === index; });
83391
83392                 var content = _body.selectAll('.field-help-content')
83393                     .html(d.html);
83394
83395                 // class the paragraphs so we can find and style them
83396                 content.selectAll('p')
83397                     .attr('class', function(d, i) { return tkeys[i]; });
83398
83399                 // insert special content for certain help sections
83400                 if (d.key === 'help.field.restrictions.inspecting') {
83401                     content
83402                         .insert('img', 'p.from_shadow')
83403                         .attr('class', 'field-help-image cf')
83404                         .attr('src', context.imagePath('tr_inspect.gif'));
83405
83406                 } else if (d.key === 'help.field.restrictions.modifying') {
83407                     content
83408                         .insert('img', 'p.allow_turn')
83409                         .attr('class', 'field-help-image cf')
83410                         .attr('src', context.imagePath('tr_modify.gif'));
83411                 }
83412             }
83413
83414
83415             fieldHelp.button = function(selection) {
83416                 if (_body.empty()) { return; }
83417
83418                 var button = selection.selectAll('.field-help-button')
83419                     .data([0]);
83420
83421                 // enter/update
83422                 button.enter()
83423                     .append('button')
83424                     .attr('class', 'field-help-button')
83425                     .attr('tabindex', -1)
83426                     .call(svgIcon('#iD-icon-help'))
83427                     .merge(button)
83428                     .on('click', function () {
83429                         event.stopPropagation();
83430                         event.preventDefault();
83431                         if (_body.classed('hide')) {
83432                             show();
83433                         } else {
83434                             hide();
83435                         }
83436                     });
83437             };
83438
83439
83440             function updatePosition() {
83441                 var wrap = _wrap.node();
83442                 var inspector = _inspector.node();
83443                 var wRect = wrap.getBoundingClientRect();
83444                 var iRect = inspector.getBoundingClientRect();
83445
83446                 _body
83447                     .style('top', wRect.top + inspector.scrollTop - iRect.top + 'px');
83448             }
83449
83450
83451             fieldHelp.body = function(selection) {
83452                 // This control expects the field to have a form-field-input-wrap div
83453                 _wrap = selection.selectAll('.form-field-input-wrap');
83454                 if (_wrap.empty()) { return; }
83455
83456                 // absolute position relative to the inspector, so it "floats" above the fields
83457                 _inspector = context.container().select('.sidebar .entity-editor-pane .inspector-body');
83458                 if (_inspector.empty()) { return; }
83459
83460                 _body = _inspector.selectAll('.field-help-body')
83461                     .data([0]);
83462
83463                 var enter = _body.enter()
83464                     .append('div')
83465                     .attr('class', 'field-help-body hide');   // initially hidden
83466
83467                 var titleEnter = enter
83468                     .append('div')
83469                     .attr('class', 'field-help-title cf');
83470
83471                 titleEnter
83472                     .append('h2')
83473                     .attr('class', ((_mainLocalizer.textDirection() === 'rtl') ? 'fr' : 'fl'))
83474                     .text(_t('help.field.' + fieldName + '.title'));
83475
83476                 titleEnter
83477                     .append('button')
83478                     .attr('class', 'fr close')
83479                     .on('click', function() {
83480                         event.stopPropagation();
83481                         event.preventDefault();
83482                         hide();
83483                     })
83484                     .call(svgIcon('#iD-icon-close'));
83485
83486                 var navEnter = enter
83487                     .append('div')
83488                     .attr('class', 'field-help-nav cf');
83489
83490                 var titles = docs.map(function(d) { return d.title; });
83491                 navEnter.selectAll('.field-help-nav-item')
83492                     .data(titles)
83493                     .enter()
83494                     .append('div')
83495                     .attr('class', 'field-help-nav-item')
83496                     .text(function(d) { return d; })
83497                     .on('click', function(d, i) {
83498                         event.stopPropagation();
83499                         event.preventDefault();
83500                         clickHelp(i);
83501                     });
83502
83503                 enter
83504                     .append('div')
83505                     .attr('class', 'field-help-content');
83506
83507                 _body = _body
83508                     .merge(enter);
83509
83510                 clickHelp(0);
83511             };
83512
83513
83514             return fieldHelp;
83515         }
83516
83517         function uiFieldCheck(field, context) {
83518             var dispatch$1 = dispatch('change');
83519             var options = field.strings && field.strings.options;
83520             var values = [];
83521             var texts = [];
83522
83523             var _tags;
83524
83525             var input = select(null);
83526             var text = select(null);
83527             var label = select(null);
83528             var reverser = select(null);
83529
83530             var _impliedYes;
83531             var _entityIDs = [];
83532             var _value;
83533
83534
83535             if (options) {
83536                 for (var k in options) {
83537                     values.push(k === 'undefined' ? undefined : k);
83538                     texts.push(field.t('options.' + k, { 'default': options[k] }));
83539                 }
83540             } else {
83541                 values = [undefined, 'yes'];
83542                 texts = [_t('inspector.unknown'), _t('inspector.check.yes')];
83543                 if (field.type !== 'defaultCheck') {
83544                     values.push('no');
83545                     texts.push(_t('inspector.check.no'));
83546                 }
83547             }
83548
83549
83550             // Checks tags to see whether an undefined value is "Assumed to be Yes"
83551             function checkImpliedYes() {
83552                 _impliedYes = (field.id === 'oneway_yes');
83553
83554                 // hack: pretend `oneway` field is a `oneway_yes` field
83555                 // where implied oneway tag exists (e.g. `junction=roundabout`) #2220, #1841
83556                 if (field.id === 'oneway') {
83557                     var entity = context.entity(_entityIDs[0]);
83558                     for (var key in entity.tags) {
83559                         if (key in osmOneWayTags && (entity.tags[key] in osmOneWayTags[key])) {
83560                             _impliedYes = true;
83561                             texts[0] = _t('presets.fields.oneway_yes.options.undefined');
83562                             break;
83563                         }
83564                     }
83565                 }
83566             }
83567
83568
83569             function reverserHidden() {
83570                 if (!context.container().select('div.inspector-hover').empty()) { return true; }
83571                 return !(_value === 'yes' || (_impliedYes && !_value));
83572             }
83573
83574
83575             function reverserSetText(selection) {
83576                 var entity = _entityIDs.length && context.hasEntity(_entityIDs[0]);
83577                 if (reverserHidden() || !entity) { return selection; }
83578
83579                 var first = entity.first();
83580                 var last = entity.isClosed() ? entity.nodes[entity.nodes.length - 2] : entity.last();
83581                 var pseudoDirection = first < last;
83582                 var icon = pseudoDirection ? '#iD-icon-forward' : '#iD-icon-backward';
83583
83584                 selection.selectAll('.reverser-span')
83585                     .text(_t('inspector.check.reverser'))
83586                     .call(svgIcon(icon, 'inline'));
83587
83588                 return selection;
83589             }
83590
83591
83592             var check = function(selection) {
83593                 checkImpliedYes();
83594
83595                 label = selection.selectAll('.form-field-input-wrap')
83596                     .data([0]);
83597
83598                 var enter = label.enter()
83599                     .append('label')
83600                     .attr('class', 'form-field-input-wrap form-field-input-check');
83601
83602                 enter
83603                     .append('input')
83604                     .property('indeterminate', field.type !== 'defaultCheck')
83605                     .attr('type', 'checkbox')
83606                     .attr('id', field.domId);
83607
83608                 enter
83609                     .append('span')
83610                     .text(texts[0])
83611                     .attr('class', 'value');
83612
83613                 if (field.type === 'onewayCheck') {
83614                     enter
83615                         .append('a')
83616                         .attr('class', 'reverser button' + (reverserHidden() ? ' hide' : ''))
83617                         .attr('href', '#')
83618                         .append('span')
83619                         .attr('class', 'reverser-span');
83620                 }
83621
83622                 label = label.merge(enter);
83623                 input = label.selectAll('input');
83624                 text = label.selectAll('span.value');
83625
83626                 input
83627                     .on('click', function() {
83628                         event.stopPropagation();
83629                         var t = {};
83630
83631                         if (Array.isArray(_tags[field.key])) {
83632                             if (values.indexOf('yes') !== -1) {
83633                                 t[field.key] = 'yes';
83634                             } else {
83635                                 t[field.key] = values[0];
83636                             }
83637                         } else {
83638                             t[field.key] = values[(values.indexOf(_value) + 1) % values.length];
83639                         }
83640
83641                         // Don't cycle through `alternating` or `reversible` states - #4970
83642                         // (They are supported as translated strings, but should not toggle with clicks)
83643                         if (t[field.key] === 'reversible' || t[field.key] === 'alternating') {
83644                             t[field.key] = values[0];
83645                         }
83646
83647                         dispatch$1.call('change', this, t);
83648                     });
83649
83650                 if (field.type === 'onewayCheck') {
83651                     reverser = label.selectAll('.reverser');
83652
83653                     reverser
83654                         .call(reverserSetText)
83655                         .on('click', function() {
83656                             event.preventDefault();
83657                             event.stopPropagation();
83658                             context.perform(
83659                                 function(graph) {
83660                                     for (var i in _entityIDs) {
83661                                         graph = actionReverse(_entityIDs[i])(graph);
83662                                     }
83663                                     return graph;
83664                                 },
83665                                 _t('operations.reverse.annotation')
83666                             );
83667
83668                             // must manually revalidate since no 'change' event was called
83669                             context.validator().validate();
83670
83671                             select(this)
83672                                 .call(reverserSetText);
83673                         });
83674                 }
83675             };
83676
83677
83678             check.entityIDs = function(val) {
83679                 if (!arguments.length) { return _entityIDs; }
83680                 _entityIDs = val;
83681                 return check;
83682             };
83683
83684
83685             check.tags = function(tags) {
83686
83687                 _tags = tags;
83688
83689                 function isChecked(val) {
83690                     return val !== 'no' && val !== '' && val !== undefined && val !== null;
83691                 }
83692
83693                 function textFor(val) {
83694                     if (val === '') { val = undefined; }
83695                     var index = values.indexOf(val);
83696                     return (index !== -1 ? texts[index] : ('"' + val + '"'));
83697                 }
83698
83699                 checkImpliedYes();
83700
83701                 var isMixed = Array.isArray(tags[field.key]);
83702
83703                 _value = !isMixed && tags[field.key] && tags[field.key].toLowerCase();
83704
83705                 if (field.type === 'onewayCheck' && (_value === '1' || _value === '-1')) {
83706                     _value = 'yes';
83707                 }
83708
83709                 input
83710                     .property('indeterminate', isMixed || (field.type !== 'defaultCheck' && !_value))
83711                     .property('checked', isChecked(_value));
83712
83713                 text
83714                     .text(isMixed ? _t('inspector.multiple_values') : textFor(_value))
83715                     .classed('mixed', isMixed);
83716
83717                 label
83718                     .classed('set', !!_value);
83719
83720                 if (field.type === 'onewayCheck') {
83721                     reverser
83722                         .classed('hide', reverserHidden())
83723                         .call(reverserSetText);
83724                 }
83725             };
83726
83727
83728             check.focus = function() {
83729                 input.node().focus();
83730             };
83731
83732             return utilRebind(check, dispatch$1, 'on');
83733         }
83734
83735         function uiFieldCombo(field, context) {
83736             var dispatch$1 = dispatch('change');
83737             var taginfo = services.taginfo;
83738             var isMulti = (field.type === 'multiCombo');
83739             var isNetwork = (field.type === 'networkCombo');
83740             var isSemi = (field.type === 'semiCombo');
83741             var optstrings = field.strings && field.strings.options;
83742             var optarray = field.options;
83743             var snake_case = (field.snake_case || (field.snake_case === undefined));
83744             var caseSensitive = field.caseSensitive;
83745             var combobox = uiCombobox(context, 'combo-' + field.safeid)
83746                 .caseSensitive(caseSensitive)
83747                 .minItems(isMulti || isSemi ? 1 : 2);
83748             var container = select(null);
83749             var inputWrap = select(null);
83750             var input = select(null);
83751             var _comboData = [];
83752             var _multiData = [];
83753             var _entityIDs = [];
83754             var _tags;
83755             var _countryCode;
83756             var _staticPlaceholder;
83757
83758             // initialize deprecated tags array
83759             var _dataDeprecated = [];
83760             _mainFileFetcher.get('deprecated')
83761                 .then(function(d) { _dataDeprecated = d; })
83762                 .catch(function() { /* ignore */ });
83763
83764
83765             // ensure multiCombo field.key ends with a ':'
83766             if (isMulti && /[^:]$/.test(field.key)) {
83767                 field.key += ':';
83768             }
83769
83770
83771             function snake(s) {
83772                 return s.replace(/\s+/g, '_');
83773             }
83774
83775             function unsnake(s) {
83776                 return s.replace(/_+/g, ' ');
83777             }
83778
83779             function clean(s) {
83780                 return s.split(';')
83781                     .map(function(s) { return s.trim(); })
83782                     .join(';');
83783             }
83784
83785
83786             // returns the tag value for a display value
83787             // (for multiCombo, dval should be the key suffix, not the entire key)
83788             function tagValue(dval) {
83789                 dval = clean(dval || '');
83790
83791                 if (optstrings) {
83792                     var found = _comboData.find(function(o) {
83793                         return o.key && clean(o.value) === dval;
83794                     });
83795                     if (found) {
83796                         return found.key;
83797                     }
83798                 }
83799
83800                 if (field.type === 'typeCombo' && !dval) {
83801                     return 'yes';
83802                 }
83803
83804                 return (snake_case ? snake(dval) : dval) || undefined;
83805             }
83806
83807
83808             // returns the display value for a tag value
83809             // (for multiCombo, tval should be the key suffix, not the entire key)
83810             function displayValue(tval) {
83811                 tval = tval || '';
83812
83813                 if (optstrings) {
83814                     var found = _comboData.find(function(o) {
83815                         return o.key === tval && o.value;
83816                     });
83817                     if (found) {
83818                         return found.value;
83819                     }
83820                 }
83821
83822                 if (field.type === 'typeCombo' && tval.toLowerCase() === 'yes') {
83823                     return '';
83824                 }
83825
83826                 return snake_case ? unsnake(tval) : tval;
83827             }
83828
83829
83830             // Compute the difference between arrays of objects by `value` property
83831             //
83832             // objectDifference([{value:1}, {value:2}, {value:3}], [{value:2}])
83833             // > [{value:1}, {value:3}]
83834             //
83835             function objectDifference(a, b) {
83836                 return a.filter(function(d1) {
83837                     return !b.some(function(d2) {
83838                         return !d2.isMixed && d1.value === d2.value;
83839                     });
83840                 });
83841             }
83842
83843
83844             function initCombo(selection, attachTo) {
83845                 if (optstrings) {
83846                     selection.attr('readonly', 'readonly');
83847                     selection.call(combobox, attachTo);
83848                     setStaticValues(setPlaceholder);
83849
83850                 } else if (optarray) {
83851                     selection.call(combobox, attachTo);
83852                     setStaticValues(setPlaceholder);
83853
83854                 } else if (taginfo) {
83855                     selection.call(combobox.fetcher(setTaginfoValues), attachTo);
83856                     setTaginfoValues('', setPlaceholder);
83857                 }
83858             }
83859
83860
83861             function setStaticValues(callback) {
83862                 if (!(optstrings || optarray)) { return; }
83863
83864                 if (optstrings) {
83865                     _comboData = Object.keys(optstrings).map(function(k) {
83866                         var v = field.t('options.' + k, { 'default': optstrings[k] });
83867                         return {
83868                             key: k,
83869                             value: v,
83870                             title: v
83871                         };
83872                     });
83873
83874                 } else if (optarray) {
83875                     _comboData = optarray.map(function(k) {
83876                         var v = snake_case ? unsnake(k) : k;
83877                         return {
83878                             key: k,
83879                             value: v,
83880                             title: v
83881                         };
83882                     });
83883                 }
83884
83885                 combobox.data(objectDifference(_comboData, _multiData));
83886                 if (callback) { callback(_comboData); }
83887             }
83888
83889
83890             function setTaginfoValues(q, callback) {
83891                 var fn = isMulti ? 'multikeys' : 'values';
83892                 var query = (isMulti ? field.key : '') + q;
83893                 var hasCountryPrefix = isNetwork && _countryCode && _countryCode.indexOf(q.toLowerCase()) === 0;
83894                 if (hasCountryPrefix) {
83895                     query = _countryCode + ':';
83896                 }
83897
83898                 var params = {
83899                     debounce: (q !== ''),
83900                     key: field.key,
83901                     query: query
83902                 };
83903
83904                 if (_entityIDs.length) {
83905                     params.geometry = context.graph().geometry(_entityIDs[0]);
83906                 }
83907
83908                 taginfo[fn](params, function(err, data) {
83909                     if (err) { return; }
83910
83911                     data = data.filter(function(d) {
83912
83913                         if (field.type === 'typeCombo' && d.value === 'yes') {
83914                             // don't show the fallback value
83915                             return false;
83916                         }
83917
83918                         // don't show values with very low usage
83919                         return !d.count || d.count > 10;
83920                     });
83921
83922                     var deprecatedValues = osmEntity.deprecatedTagValuesByKey(_dataDeprecated)[field.key];
83923                     if (deprecatedValues) {
83924                         // don't suggest deprecated tag values
83925                         data = data.filter(function(d) {
83926                             return deprecatedValues.indexOf(d.value) === -1;
83927                         });
83928                     }
83929
83930                     if (hasCountryPrefix) {
83931                         data = data.filter(function(d) {
83932                             return d.value.toLowerCase().indexOf(_countryCode + ':') === 0;
83933                         });
83934                     }
83935
83936                     // hide the caret if there are no suggestions
83937                     container.classed('empty-combobox', data.length === 0);
83938
83939                     _comboData = data.map(function(d) {
83940                         var k = d.value;
83941                         if (isMulti) { k = k.replace(field.key, ''); }
83942                         var v = snake_case ? unsnake(k) : k;
83943                         return {
83944                             key: k,
83945                             value: v,
83946                             title: isMulti ? v : d.title
83947                         };
83948                     });
83949
83950                     _comboData = objectDifference(_comboData, _multiData);
83951                     if (callback) { callback(_comboData); }
83952                 });
83953             }
83954
83955
83956             function setPlaceholder(values) {
83957
83958                 if (isMulti || isSemi) {
83959                     _staticPlaceholder = field.placeholder() || _t('inspector.add');
83960                 } else {
83961                     var vals = values
83962                         .map(function(d) { return d.value; })
83963                         .filter(function(s) { return s.length < 20; });
83964
83965                     var placeholders = vals.length > 1 ? vals : values.map(function(d) { return d.key; });
83966                     _staticPlaceholder = field.placeholder() || placeholders.slice(0, 3).join(', ');
83967                 }
83968
83969                 if (!/(…|\.\.\.)$/.test(_staticPlaceholder)) {
83970                     _staticPlaceholder += '…';
83971                 }
83972
83973                 var ph;
83974                 if (!isMulti && !isSemi && _tags && Array.isArray(_tags[field.key])) {
83975                     ph = _t('inspector.multiple_values');
83976                 } else {
83977                     ph =  _staticPlaceholder;
83978                 }
83979
83980                 container.selectAll('input')
83981                     .attr('placeholder', ph);
83982             }
83983
83984
83985             function change() {
83986                 var t = {};
83987                 var val;
83988
83989                 if (isMulti || isSemi) {
83990                     val = tagValue(utilGetSetValue(input).replace(/,/g, ';')) || '';
83991                     container.classed('active', false);
83992                     utilGetSetValue(input, '');
83993
83994                     var vals = val.split(';').filter(Boolean);
83995                     if (!vals.length) { return; }
83996
83997                     if (isMulti) {
83998                         utilArrayUniq(vals).forEach(function(v) {
83999                             var key = field.key + v;
84000                             if (_tags) {
84001                                 // don't set a multicombo value to 'yes' if it already has a non-'no' value
84002                                 // e.g. `language:de=main`
84003                                 var old = _tags[key];
84004                                 if (typeof old === 'string' && old.toLowerCase() !== 'no') { return; }
84005                             }
84006                             key = context.cleanTagKey(key);
84007                             field.keys.push(key);
84008                             t[key] = 'yes';
84009                         });
84010
84011                     } else if (isSemi) {
84012                         var arr = _multiData.map(function(d) { return d.key; });
84013                         arr = arr.concat(vals);
84014                         t[field.key] = context.cleanTagValue(utilArrayUniq(arr).filter(Boolean).join(';'));
84015                     }
84016
84017                     window.setTimeout(function() { input.node().focus(); }, 10);
84018
84019                 } else {
84020                     var rawValue = utilGetSetValue(input);
84021
84022                     // don't override multiple values with blank string
84023                     if (!rawValue && Array.isArray(_tags[field.key])) { return; }
84024
84025                     val = context.cleanTagValue(tagValue(rawValue));
84026                     t[field.key] = val || undefined;
84027                 }
84028
84029                 dispatch$1.call('change', this, t);
84030             }
84031
84032
84033             function removeMultikey(d) {
84034                 event.stopPropagation();
84035                 var t = {};
84036                 if (isMulti) {
84037                     t[d.key] = undefined;
84038                 } else if (isSemi) {
84039                     var arr = _multiData.map(function(md) {
84040                         return md.key === d.key ? null : md.key;
84041                     }).filter(Boolean);
84042
84043                     arr = utilArrayUniq(arr);
84044                     t[field.key] = arr.length ? arr.join(';') : undefined;
84045                 }
84046                 dispatch$1.call('change', this, t);
84047             }
84048
84049
84050             function combo(selection) {
84051                 container = selection.selectAll('.form-field-input-wrap')
84052                     .data([0]);
84053
84054                 var type = (isMulti || isSemi) ? 'multicombo': 'combo';
84055                 container = container.enter()
84056                     .append('div')
84057                     .attr('class', 'form-field-input-wrap form-field-input-' + type)
84058                     .merge(container);
84059
84060                 if (isMulti || isSemi) {
84061                     container = container.selectAll('.chiplist')
84062                         .data([0]);
84063
84064                     var listClass = 'chiplist';
84065
84066                     // Use a separate line for each value in the Destinations field
84067                     // to mimic highway exit signs
84068                     if (field.key === 'destination') {
84069                         listClass += ' full-line-chips';
84070                     }
84071
84072                     container = container.enter()
84073                         .append('ul')
84074                         .attr('class', listClass)
84075                         .on('click', function() {
84076                             window.setTimeout(function() { input.node().focus(); }, 10);
84077                         })
84078                         .merge(container);
84079
84080
84081                     inputWrap = container.selectAll('.input-wrap')
84082                         .data([0]);
84083
84084                     inputWrap = inputWrap.enter()
84085                         .append('li')
84086                         .attr('class', 'input-wrap')
84087                         .merge(inputWrap);
84088
84089                     input = inputWrap.selectAll('input')
84090                         .data([0]);
84091                 } else {
84092                     input = container.selectAll('input')
84093                         .data([0]);
84094                 }
84095
84096                 input = input.enter()
84097                     .append('input')
84098                     .attr('type', 'text')
84099                     .attr('id', field.domId)
84100                     .call(utilNoAuto)
84101                     .call(initCombo, selection)
84102                     .merge(input);
84103
84104                 if (isNetwork) {
84105                     var extent = combinedEntityExtent();
84106                     var countryCode = extent && iso1A2Code(extent.center());
84107                     _countryCode = countryCode && countryCode.toLowerCase();
84108                 }
84109
84110                 input
84111                     .on('change', change)
84112                     .on('blur', change);
84113
84114                 input
84115                     .on('keydown.field', function() {
84116                         switch (event.keyCode) {
84117                             case 13: // ↩ Return
84118                                 input.node().blur(); // blurring also enters the value
84119                                 event.stopPropagation();
84120                                 break;
84121                         }
84122                     });
84123
84124                 if (isMulti || isSemi) {
84125                     combobox
84126                         .on('accept', function() {
84127                             input.node().blur();
84128                             input.node().focus();
84129                         });
84130
84131                     input
84132                         .on('focus', function() { container.classed('active', true); });
84133                 }
84134             }
84135
84136
84137             combo.tags = function(tags) {
84138                 _tags = tags;
84139
84140                 if (isMulti || isSemi) {
84141                     _multiData = [];
84142
84143                     var maxLength;
84144
84145                     if (isMulti) {
84146                         // Build _multiData array containing keys already set..
84147                         for (var k in tags) {
84148                             if (k.indexOf(field.key) !== 0) { continue; }
84149                             var v = tags[k];
84150                             if (!v || (typeof v === 'string' && v.toLowerCase() === 'no')) { continue; }
84151
84152                             var suffix = k.substring(field.key.length);
84153                             _multiData.push({
84154                                 key: k,
84155                                 value: displayValue(suffix),
84156                                 isMixed: Array.isArray(v)
84157                             });
84158                         }
84159
84160                         // Set keys for form-field modified (needed for undo and reset buttons)..
84161                         field.keys = _multiData.map(function(d) { return d.key; });
84162
84163                         // limit the input length so it fits after prepending the key prefix
84164                         maxLength = context.maxCharsForTagKey() - utilUnicodeCharsCount(field.key);
84165
84166                     } else if (isSemi) {
84167
84168                         var allValues = [];
84169                         var commonValues;
84170                         if (Array.isArray(tags[field.key])) {
84171
84172                             tags[field.key].forEach(function(tagVal) {
84173                                 var thisVals = utilArrayUniq((tagVal || '').split(';')).filter(Boolean);
84174                                 allValues = allValues.concat(thisVals);
84175                                 if (!commonValues) {
84176                                     commonValues = thisVals;
84177                                 } else {
84178                                     commonValues = commonValues.filter(function (value) { return thisVals.includes(value); });
84179                                 }
84180                             });
84181                             allValues = utilArrayUniq(allValues).filter(Boolean);
84182
84183                         } else {
84184                             allValues =  utilArrayUniq((tags[field.key] || '').split(';')).filter(Boolean);
84185                             commonValues = allValues;
84186                         }
84187
84188                         _multiData = allValues.map(function(v) {
84189                             return {
84190                                 key: v,
84191                                 value: displayValue(v),
84192                                 isMixed: !commonValues.includes(v)
84193                             };
84194                         });
84195
84196                         var currLength = utilUnicodeCharsCount(commonValues.join(';'));
84197
84198                         // limit the input length to the remaining available characters
84199                         maxLength = context.maxCharsForTagValue() - currLength;
84200
84201                         if (currLength > 0) {
84202                             // account for the separator if a new value will be appended to existing
84203                             maxLength -= 1;
84204                         }
84205                     }
84206                     // a negative maxlength doesn't make sense
84207                     maxLength = Math.max(0, maxLength);
84208
84209                     var allowDragAndDrop = isSemi // only semiCombo values are ordered
84210                         && !Array.isArray(tags[field.key]);
84211
84212                     // Exclude existing multikeys from combo options..
84213                     var available = objectDifference(_comboData, _multiData);
84214                     combobox.data(available);
84215
84216                     // Hide 'Add' button if this field uses fixed set of
84217                     // translateable optstrings and they're all currently used,
84218                     // or if the field is already at its character limit
84219                     var hideAdd = (optstrings && !available.length) || maxLength <= 0;
84220                     container.selectAll('.chiplist .input-wrap')
84221                         .style('display', hideAdd ? 'none' : null);
84222
84223
84224                     // Render chips
84225                     var chips = container.selectAll('.chip')
84226                         .data(_multiData);
84227
84228                     chips.exit()
84229                         .remove();
84230
84231                     var enter = chips.enter()
84232                         .insert('li', '.input-wrap')
84233                         .attr('class', 'chip');
84234
84235                     enter.append('span');
84236                     enter.append('a');
84237
84238                     chips = chips.merge(enter)
84239                         .order()
84240                         .classed('draggable', allowDragAndDrop)
84241                         .classed('mixed', function(d) {
84242                             return d.isMixed;
84243                         })
84244                         .attr('title', function(d) {
84245                             return d.isMixed ? _t('inspector.unshared_value_tooltip') : null;
84246                         });
84247
84248                     if (allowDragAndDrop) {
84249                         registerDragAndDrop(chips);
84250                     }
84251
84252                     chips.select('span')
84253                         .text(function(d) { return d.value; });
84254
84255                     chips.select('a')
84256                         .on('click', removeMultikey)
84257                         .attr('class', 'remove')
84258                         .text('×');
84259
84260                 } else {
84261                     var isMixed = Array.isArray(tags[field.key]);
84262
84263                     var mixedValues = isMixed && tags[field.key].map(function(val) {
84264                         return displayValue(val);
84265                     }).filter(Boolean);
84266
84267                     utilGetSetValue(input, !isMixed ? displayValue(tags[field.key]) : '')
84268                         .attr('title', isMixed ? mixedValues.join('\n') : undefined)
84269                         .attr('placeholder', isMixed ? _t('inspector.multiple_values') : _staticPlaceholder || '')
84270                         .classed('mixed', isMixed);
84271                 }
84272             };
84273
84274             function registerDragAndDrop(selection) {
84275
84276                 // allow drag and drop re-ordering of chips
84277                 var dragOrigin, targetIndex;
84278                 selection.call(d3_drag()
84279                     .on('start', function() {
84280                         dragOrigin = {
84281                             x: event.x,
84282                             y: event.y
84283                         };
84284                         targetIndex = null;
84285                     })
84286                     .on('drag', function(d, index) {
84287                         var x = event.x - dragOrigin.x,
84288                             y = event.y - dragOrigin.y;
84289
84290                         if (!select(this).classed('dragging') &&
84291                             // don't display drag until dragging beyond a distance threshold
84292                             Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)) <= 5) { return; }
84293
84294                         select(this)
84295                             .classed('dragging', true);
84296
84297                         targetIndex = null;
84298                         var targetIndexOffsetTop = null;
84299                         var draggedTagWidth = select(this).node().offsetWidth;
84300
84301                         if (field.key === 'destination') { // meaning tags are full width
84302                             container.selectAll('.chip')
84303                                 .style('transform', function(d2, index2) {
84304                                     var node = select(this).node();
84305
84306                                     if (index === index2) {
84307                                         return 'translate(' + x + 'px, ' + y + 'px)';
84308                                     // move the dragged tag up the order
84309                                     } else if (index2 > index && event.y > node.offsetTop) {
84310                                         if (targetIndex === null || index2 > targetIndex) {
84311                                             targetIndex = index2;
84312                                         }
84313                                         return 'translateY(-100%)';
84314                                     // move the dragged tag down the order
84315                                     } else if (index2 < index && event.y < node.offsetTop + node.offsetHeight) {
84316                                         if (targetIndex === null || index2 < targetIndex) {
84317                                             targetIndex = index2;
84318                                         }
84319                                         return 'translateY(100%)';
84320                                     }
84321                                     return null;
84322                                 });
84323                         } else {
84324                             container.selectAll('.chip')
84325                                 .each(function(d2, index2) {
84326                                     var node = select(this).node();
84327
84328                                     // check the cursor is in the bounding box
84329                                     if (
84330                                         index !== index2 &&
84331                                         event.x < node.offsetLeft + node.offsetWidth + 5 &&
84332                                         event.x > node.offsetLeft &&
84333                                         event.y < node.offsetTop + node.offsetHeight &&
84334                                         event.y > node.offsetTop
84335                                     ) {
84336                                         targetIndex = index2;
84337                                         targetIndexOffsetTop = node.offsetTop;
84338                                     }
84339                                 })
84340                                 .style('transform', function(d2, index2) {
84341                                     var node = select(this).node();
84342
84343                                     if (index === index2) {
84344                                         return 'translate(' + x + 'px, ' + y + 'px)';
84345                                     }
84346
84347                                     // only translate tags in the same row
84348                                     if (node.offsetTop === targetIndexOffsetTop) {
84349                                         if (index2 < index && index2 >= targetIndex) {
84350                                             return 'translateX(' + draggedTagWidth + 'px)';
84351                                         } else if (index2 > index && index2 <= targetIndex) {
84352                                             return 'translateX(-' + draggedTagWidth + 'px)';
84353                                         }
84354                                     }
84355                                     return null;
84356                                 });
84357                             }
84358                     })
84359                     .on('end', function(d, index) {
84360                         if (!select(this).classed('dragging')) {
84361                             return;
84362                         }
84363
84364                         select(this)
84365                             .classed('dragging', false);
84366
84367                         container.selectAll('.chip')
84368                             .style('transform', null);
84369
84370                         if (typeof targetIndex === 'number') {
84371                             var element = _multiData[index];
84372                             _multiData.splice(index, 1);
84373                             _multiData.splice(targetIndex, 0, element);
84374
84375                             var t = {};
84376
84377                             if (_multiData.length) {
84378                                 t[field.key] = _multiData.map(function(element) {
84379                                     return element.key;
84380                                 }).join(';');
84381                             } else {
84382                                 t[field.key] = undefined;
84383                             }
84384
84385                             dispatch$1.call('change', this, t);
84386                         }
84387                         dragOrigin = undefined;
84388                         targetIndex = undefined;
84389                     })
84390                 );
84391             }
84392
84393
84394             combo.focus = function() {
84395                 input.node().focus();
84396             };
84397
84398
84399             combo.entityIDs = function(val) {
84400                 if (!arguments.length) { return _entityIDs; }
84401                 _entityIDs = val;
84402                 return combo;
84403             };
84404
84405
84406             function combinedEntityExtent() {
84407                 return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph());
84408             }
84409
84410
84411             return utilRebind(combo, dispatch$1, 'on');
84412         }
84413
84414         function uiFieldText(field, context) {
84415             var dispatch$1 = dispatch('change');
84416             var input = select(null);
84417             var outlinkButton = select(null);
84418             var _entityIDs = [];
84419             var _tags;
84420             var _phoneFormats = {};
84421
84422             if (field.type === 'tel') {
84423                 _mainFileFetcher.get('phone_formats')
84424                     .then(function(d) {
84425                         _phoneFormats = d;
84426                         updatePhonePlaceholder();
84427                     })
84428                     .catch(function() { /* ignore */ });
84429             }
84430
84431             function i(selection) {
84432                 var entity = _entityIDs.length && context.hasEntity(_entityIDs[0]);
84433                 var preset = entity && _mainPresetIndex.match(entity, context.graph());
84434                 var isLocked = preset && preset.suggestion && field.id === 'brand';
84435                 field.locked(isLocked);
84436
84437                 var wrap = selection.selectAll('.form-field-input-wrap')
84438                     .data([0]);
84439
84440                 wrap = wrap.enter()
84441                     .append('div')
84442                     .attr('class', 'form-field-input-wrap form-field-input-' + field.type)
84443                     .merge(wrap);
84444
84445                 input = wrap.selectAll('input')
84446                     .data([0]);
84447
84448                 input = input.enter()
84449                     .append('input')
84450                     .attr('type', field.type === 'identifier' ? 'text' : field.type)
84451                     .attr('id', field.domId)
84452                     .classed(field.type, true)
84453                     .call(utilNoAuto)
84454                     .merge(input);
84455
84456                 input
84457                     .classed('disabled', !!isLocked)
84458                     .attr('readonly', isLocked || null)
84459                     .on('input', change(true))
84460                     .on('blur', change())
84461                     .on('change', change());
84462
84463
84464                 if (field.type === 'tel') {
84465                     updatePhonePlaceholder();
84466
84467                 } else if (field.type === 'number') {
84468                     var rtl = (_mainLocalizer.textDirection() === 'rtl');
84469
84470                     input.attr('type', 'text');
84471
84472                     var buttons = wrap.selectAll('.increment, .decrement')
84473                         .data(rtl ? [1, -1] : [-1, 1]);
84474
84475                     buttons.enter()
84476                         .append('button')
84477                         .attr('tabindex', -1)
84478                         .attr('class', function(d) {
84479                             var which = (d === 1 ? 'increment' : 'decrement');
84480                             return 'form-field-button ' + which;
84481                         })
84482                         .merge(buttons)
84483                         .on('click', function(d) {
84484                             event.preventDefault();
84485                             var raw_vals = input.node().value || '0';
84486                             var vals = raw_vals.split(';');
84487                             vals = vals.map(function(v) {
84488                                 var num = parseFloat(v.trim(), 10);
84489                                 return isFinite(num) ? clamped(num + d) : v.trim();
84490                             });
84491                             input.node().value = vals.join(';');
84492                             change()();
84493                         });
84494                 } else if (field.type === 'identifier' && field.urlFormat && field.pattern) {
84495
84496                     input.attr('type', 'text');
84497
84498                     outlinkButton = wrap.selectAll('.foreign-id-permalink')
84499                         .data([0]);
84500
84501                     outlinkButton.enter()
84502                         .append('button')
84503                         .attr('tabindex', -1)
84504                         .call(svgIcon('#iD-icon-out-link'))
84505                         .attr('class', 'form-field-button foreign-id-permalink')
84506                         .attr('title', function() {
84507                             var domainResults = /^https?:\/\/(.{1,}?)\//.exec(field.urlFormat);
84508                             if (domainResults.length >= 2 && domainResults[1]) {
84509                                 var domain = domainResults[1];
84510                                 return _t('icons.view_on', { domain: domain });
84511                             }
84512                             return '';
84513                         })
84514                         .on('click', function() {
84515                             event.preventDefault();
84516
84517                             var value = validIdentifierValueForLink();
84518                             if (value) {
84519                                 var url = field.urlFormat.replace(/{value}/, encodeURIComponent(value));
84520                                 window.open(url, '_blank');
84521                             }
84522                         })
84523                         .merge(outlinkButton);
84524                 }
84525             }
84526
84527
84528             function updatePhonePlaceholder() {
84529                 if (input.empty() || !Object.keys(_phoneFormats).length) { return; }
84530
84531                 var extent = combinedEntityExtent();
84532                 var countryCode = extent && iso1A2Code(extent.center());
84533                 var format = countryCode && _phoneFormats[countryCode.toLowerCase()];
84534                 if (format) { input.attr('placeholder', format); }
84535             }
84536
84537
84538             function validIdentifierValueForLink() {
84539                 if (field.type === 'identifier' && field.pattern) {
84540                     var value = utilGetSetValue(input).trim().split(';')[0];
84541                     return value && value.match(new RegExp(field.pattern));
84542                 }
84543                 return null;
84544             }
84545
84546
84547             // clamp number to min/max
84548             function clamped(num) {
84549                 if (field.minValue !== undefined) {
84550                     num = Math.max(num, field.minValue);
84551                 }
84552                 if (field.maxValue !== undefined) {
84553                     num = Math.min(num, field.maxValue);
84554                 }
84555                 return num;
84556             }
84557
84558
84559             function change(onInput) {
84560                 return function() {
84561                     var t = {};
84562                     var val = utilGetSetValue(input);
84563                     if (!onInput) { val = context.cleanTagValue(val); }
84564
84565                     // don't override multiple values with blank string
84566                     if (!val && Array.isArray(_tags[field.key])) { return; }
84567
84568                     if (!onInput) {
84569                         if (field.type === 'number' && val) {
84570                             var vals = val.split(';');
84571                             vals = vals.map(function(v) {
84572                                 var num = parseFloat(v.trim(), 10);
84573                                 return isFinite(num) ? clamped(num) : v.trim();
84574                             });
84575                             val = vals.join(';');
84576                         }
84577                         utilGetSetValue(input, val);
84578                     }
84579                     t[field.key] = val || undefined;
84580                     dispatch$1.call('change', this, t, onInput);
84581                 };
84582             }
84583
84584
84585             i.entityIDs = function(val) {
84586                 if (!arguments.length) { return _entityIDs; }
84587                 _entityIDs = val;
84588                 return i;
84589             };
84590
84591
84592             i.tags = function(tags) {
84593                 _tags = tags;
84594
84595                 var isMixed = Array.isArray(tags[field.key]);
84596
84597                 utilGetSetValue(input, !isMixed && tags[field.key] ? tags[field.key] : '')
84598                     .attr('title', isMixed ? tags[field.key].filter(Boolean).join('\n') : undefined)
84599                     .attr('placeholder', isMixed ? _t('inspector.multiple_values') : (field.placeholder() || _t('inspector.unknown')))
84600                     .classed('mixed', isMixed);
84601
84602                 if (outlinkButton && !outlinkButton.empty()) {
84603                     var disabled = !validIdentifierValueForLink();
84604                     outlinkButton.classed('disabled', disabled);
84605                 }
84606             };
84607
84608
84609             i.focus = function() {
84610                 var node = input.node();
84611                 if (node) { node.focus(); }
84612             };
84613
84614             function combinedEntityExtent() {
84615                 return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph());
84616             }
84617
84618             return utilRebind(i, dispatch$1, 'on');
84619         }
84620
84621         function uiFieldAccess(field, context) {
84622             var dispatch$1 = dispatch('change');
84623             var items = select(null);
84624             var _tags;
84625
84626             function access(selection) {
84627                 var wrap = selection.selectAll('.form-field-input-wrap')
84628                     .data([0]);
84629
84630                 wrap = wrap.enter()
84631                     .append('div')
84632                     .attr('class', 'form-field-input-wrap form-field-input-' + field.type)
84633                     .merge(wrap);
84634
84635                 var list = wrap.selectAll('ul')
84636                     .data([0]);
84637
84638                 list = list.enter()
84639                     .append('ul')
84640                     .attr('class', 'rows')
84641                     .merge(list);
84642
84643
84644                 items = list.selectAll('li')
84645                     .data(field.keys);
84646
84647                 // Enter
84648                 var enter = items.enter()
84649                     .append('li')
84650                     .attr('class', function(d) { return 'labeled-input preset-access-' + d; });
84651
84652                 enter
84653                     .append('span')
84654                     .attr('class', 'label preset-label-access')
84655                     .attr('for', function(d) { return 'preset-input-access-' + d; })
84656                     .text(function(d) { return field.t('types.' + d); });
84657
84658                 enter
84659                     .append('div')
84660                     .attr('class', 'preset-input-access-wrap')
84661                     .append('input')
84662                     .attr('type', 'text')
84663                     .attr('class', function(d) { return 'preset-input-access preset-input-access-' + d; })
84664                     .call(utilNoAuto)
84665                     .each(function(d) {
84666                         select(this)
84667                             .call(uiCombobox(context, 'access-' + d)
84668                                 .data(access.options(d))
84669                             );
84670                     });
84671
84672
84673                 // Update
84674                 items = items.merge(enter);
84675
84676                 wrap.selectAll('.preset-input-access')
84677                     .on('change', change)
84678                     .on('blur', change);
84679             }
84680
84681
84682             function change(d) {
84683                 var tag = {};
84684                 var value = context.cleanTagValue(utilGetSetValue(select(this)));
84685
84686                 // don't override multiple values with blank string
84687                 if (!value && typeof _tags[d] !== 'string') { return; }
84688
84689                 tag[d] = value || undefined;
84690                 dispatch$1.call('change', this, tag);
84691             }
84692
84693
84694             access.options = function(type) {
84695                 var options = ['no', 'permissive', 'private', 'permit', 'destination'];
84696
84697                 if (type !== 'access') {
84698                     options.unshift('yes');
84699                     options.push('designated');
84700
84701                     if (type === 'bicycle') {
84702                         options.push('dismount');
84703                     }
84704                 }
84705
84706                 return options.map(function(option) {
84707                     return {
84708                         title: field.t('options.' + option + '.description'),
84709                         value: option
84710                     };
84711                 });
84712             };
84713
84714
84715             var placeholdersByHighway = {
84716                 footway: {
84717                     foot: 'designated',
84718                     motor_vehicle: 'no'
84719                 },
84720                 steps: {
84721                     foot: 'yes',
84722                     motor_vehicle: 'no',
84723                     bicycle: 'no',
84724                     horse: 'no'
84725                 },
84726                 pedestrian: {
84727                     foot: 'yes',
84728                     motor_vehicle: 'no'
84729                 },
84730                 cycleway: {
84731                     motor_vehicle: 'no',
84732                     bicycle: 'designated'
84733                 },
84734                 bridleway: {
84735                     motor_vehicle: 'no',
84736                     horse: 'designated'
84737                 },
84738                 path: {
84739                     foot: 'yes',
84740                     motor_vehicle: 'no',
84741                     bicycle: 'yes',
84742                     horse: 'yes'
84743                 },
84744                 motorway: {
84745                     foot: 'no',
84746                     motor_vehicle: 'yes',
84747                     bicycle: 'no',
84748                     horse: 'no'
84749                 },
84750                 trunk: {
84751                     motor_vehicle: 'yes'
84752                 },
84753                 primary: {
84754                     foot: 'yes',
84755                     motor_vehicle: 'yes',
84756                     bicycle: 'yes',
84757                     horse: 'yes'
84758                 },
84759                 secondary: {
84760                     foot: 'yes',
84761                     motor_vehicle: 'yes',
84762                     bicycle: 'yes',
84763                     horse: 'yes'
84764                 },
84765                 tertiary: {
84766                     foot: 'yes',
84767                     motor_vehicle: 'yes',
84768                     bicycle: 'yes',
84769                     horse: 'yes'
84770                 },
84771                 residential: {
84772                     foot: 'yes',
84773                     motor_vehicle: 'yes',
84774                     bicycle: 'yes',
84775                     horse: 'yes'
84776                 },
84777                 unclassified: {
84778                     foot: 'yes',
84779                     motor_vehicle: 'yes',
84780                     bicycle: 'yes',
84781                     horse: 'yes'
84782                 },
84783                 service: {
84784                     foot: 'yes',
84785                     motor_vehicle: 'yes',
84786                     bicycle: 'yes',
84787                     horse: 'yes'
84788                 },
84789                 motorway_link: {
84790                     foot: 'no',
84791                     motor_vehicle: 'yes',
84792                     bicycle: 'no',
84793                     horse: 'no'
84794                 },
84795                 trunk_link: {
84796                     motor_vehicle: 'yes'
84797                 },
84798                 primary_link: {
84799                     foot: 'yes',
84800                     motor_vehicle: 'yes',
84801                     bicycle: 'yes',
84802                     horse: 'yes'
84803                 },
84804                 secondary_link: {
84805                     foot: 'yes',
84806                     motor_vehicle: 'yes',
84807                     bicycle: 'yes',
84808                     horse: 'yes'
84809                 },
84810                 tertiary_link: {
84811                     foot: 'yes',
84812                     motor_vehicle: 'yes',
84813                     bicycle: 'yes',
84814                     horse: 'yes'
84815                 }
84816             };
84817
84818
84819             access.tags = function(tags) {
84820                 _tags = tags;
84821
84822                 utilGetSetValue(items.selectAll('.preset-input-access'), function(d) {
84823                         return typeof tags[d] === 'string' ? tags[d] : '';
84824                     })
84825                     .classed('mixed', function(d) {
84826                         return tags[d] && Array.isArray(tags[d]);
84827                     })
84828                     .attr('title', function(d) {
84829                         return tags[d] && Array.isArray(tags[d]) && tags[d].filter(Boolean).join('\n');
84830                     })
84831                     .attr('placeholder', function(d) {
84832                         if (tags[d] && Array.isArray(tags[d])) {
84833                             return _t('inspector.multiple_values');
84834                         }
84835                         if (d === 'access') {
84836                             return 'yes';
84837                         }
84838                         if (tags.access && typeof tags.access === 'string') {
84839                             return tags.access;
84840                         }
84841                         if (tags.highway) {
84842                             if (typeof tags.highway === 'string') {
84843                                 if (placeholdersByHighway[tags.highway] &&
84844                                     placeholdersByHighway[tags.highway][d]) {
84845
84846                                     return placeholdersByHighway[tags.highway][d];
84847                                 }
84848                             } else {
84849                                 var impliedAccesses = tags.highway.filter(Boolean).map(function(highwayVal) {
84850                                     return placeholdersByHighway[highwayVal] && placeholdersByHighway[highwayVal][d];
84851                                 }).filter(Boolean);
84852
84853                                 if (impliedAccesses.length === tags.highway.length &&
84854                                     new Set(impliedAccesses).size === 1) {
84855                                     // if all the highway values have the same implied access for this type then use that
84856                                     return impliedAccesses[0];
84857                                 }
84858                             }
84859                         }
84860                         return field.placeholder();
84861                     });
84862             };
84863
84864
84865             access.focus = function() {
84866                 items.selectAll('.preset-input-access')
84867                     .node().focus();
84868             };
84869
84870
84871             return utilRebind(access, dispatch$1, 'on');
84872         }
84873
84874         function uiFieldAddress(field, context) {
84875             var dispatch$1 = dispatch('change');
84876             var _selection = select(null);
84877             var _wrap = select(null);
84878             var addrField = _mainPresetIndex.field('address');   // needed for placeholder strings
84879
84880             var _entityIDs = [];
84881             var _tags;
84882             var _countryCode;
84883             var _addressFormats = [{
84884                 format: [
84885                     ['housenumber', 'street'],
84886                     ['city', 'postcode']
84887                 ]
84888               }];
84889
84890             _mainFileFetcher.get('address_formats')
84891                 .then(function(d) {
84892                     _addressFormats = d;
84893                     if (!_selection.empty()) {
84894                         _selection.call(address);
84895                     }
84896                 })
84897                 .catch(function() { /* ignore */ });
84898
84899
84900             function getNearStreets() {
84901                 var extent = combinedEntityExtent();
84902                 var l = extent.center();
84903                 var box = geoExtent(l).padByMeters(200);
84904
84905                 var streets = context.history().intersects(box)
84906                     .filter(isAddressable)
84907                     .map(function(d) {
84908                         var loc = context.projection([
84909                             (extent[0][0] + extent[1][0]) / 2,
84910                             (extent[0][1] + extent[1][1]) / 2
84911                         ]);
84912                         var choice = geoChooseEdge(context.graph().childNodes(d), loc, context.projection);
84913
84914                         return {
84915                             title: d.tags.name,
84916                             value: d.tags.name,
84917                             dist: choice.distance
84918                         };
84919                     })
84920                     .sort(function(a, b) {
84921                         return a.dist - b.dist;
84922                     });
84923
84924                 return utilArrayUniqBy(streets, 'value');
84925
84926                 function isAddressable(d) {
84927                     return d.tags.highway && d.tags.name && d.type === 'way';
84928                 }
84929             }
84930
84931
84932             function getNearCities() {
84933                 var extent = combinedEntityExtent();
84934                 var l = extent.center();
84935                 var box = geoExtent(l).padByMeters(200);
84936
84937                 var cities = context.history().intersects(box)
84938                     .filter(isAddressable)
84939                     .map(function(d) {
84940                         return {
84941                             title: d.tags['addr:city'] || d.tags.name,
84942                             value: d.tags['addr:city'] || d.tags.name,
84943                             dist: geoSphericalDistance(d.extent(context.graph()).center(), l)
84944                         };
84945                     })
84946                     .sort(function(a, b) {
84947                         return a.dist - b.dist;
84948                     });
84949
84950                 return utilArrayUniqBy(cities, 'value');
84951
84952
84953                 function isAddressable(d) {
84954                     if (d.tags.name) {
84955                         if (d.tags.admin_level === '8' && d.tags.boundary === 'administrative')
84956                             { return true; }
84957                         if (d.tags.border_type === 'city')
84958                             { return true; }
84959                         if (d.tags.place === 'city' || d.tags.place === 'town' || d.tags.place === 'village')
84960                             { return true; }
84961                     }
84962
84963                     if (d.tags['addr:city'])
84964                         { return true; }
84965
84966                     return false;
84967                 }
84968             }
84969
84970             function getNearValues(key) {
84971                 var extent = combinedEntityExtent();
84972                 var l = extent.center();
84973                 var box = geoExtent(l).padByMeters(200);
84974
84975                 var results = context.history().intersects(box)
84976                     .filter(function hasTag(d) { return _entityIDs.indexOf(d.id) === -1 && d.tags[key]; })
84977                     .map(function(d) {
84978                         return {
84979                             title: d.tags[key],
84980                             value: d.tags[key],
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(results, 'value');
84989             }
84990
84991
84992             function updateForCountryCode() {
84993
84994                 if (!_countryCode) { return; }
84995
84996                 var addressFormat;
84997                 for (var i = 0; i < _addressFormats.length; i++) {
84998                     var format = _addressFormats[i];
84999                     if (!format.countryCodes) {
85000                         addressFormat = format;   // choose the default format, keep going
85001                     } else if (format.countryCodes.indexOf(_countryCode) !== -1) {
85002                         addressFormat = format;   // choose the country format, stop here
85003                         break;
85004                     }
85005                 }
85006
85007                 var dropdowns = addressFormat.dropdowns || [
85008                     'city', 'county', 'country', 'district', 'hamlet',
85009                     'neighbourhood', 'place', 'postcode', 'province',
85010                     'quarter', 'state', 'street', 'subdistrict', 'suburb'
85011                 ];
85012
85013                 var widths = addressFormat.widths || {
85014                     housenumber: 1/3, street: 2/3,
85015                     city: 2/3, state: 1/4, postcode: 1/3
85016                 };
85017
85018                 function row(r) {
85019                     // Normalize widths.
85020                     var total = r.reduce(function(sum, key) {
85021                         return sum + (widths[key] || 0.5);
85022                     }, 0);
85023
85024                     return r.map(function(key) {
85025                         return {
85026                             id: key,
85027                             width: (widths[key] || 0.5) / total
85028                         };
85029                     });
85030                 }
85031
85032                 var rows = _wrap.selectAll('.addr-row')
85033                     .data(addressFormat.format, function(d) {
85034                         return d.toString();
85035                     });
85036
85037                 rows.exit()
85038                     .remove();
85039
85040                 rows
85041                     .enter()
85042                     .append('div')
85043                     .attr('class', 'addr-row')
85044                     .selectAll('input')
85045                     .data(row)
85046                     .enter()
85047                     .append('input')
85048                     .property('type', 'text')
85049                     .call(updatePlaceholder)
85050                     .attr('class', function (d) { return 'addr-' + d.id; })
85051                     .call(utilNoAuto)
85052                     .each(addDropdown)
85053                     .style('width', function (d) { return d.width * 100 + '%'; });
85054
85055
85056                 function addDropdown(d) {
85057                     if (dropdowns.indexOf(d.id) === -1) { return; }  // not a dropdown
85058
85059                     var nearValues = (d.id === 'street') ? getNearStreets
85060                         : (d.id === 'city') ? getNearCities
85061                         : getNearValues;
85062
85063                     select(this)
85064                         .call(uiCombobox(context, 'address-' + d.id)
85065                             .minItems(1)
85066                             .caseSensitive(true)
85067                             .fetcher(function(value, callback) {
85068                                 callback(nearValues('addr:' + d.id));
85069                             })
85070                         );
85071                 }
85072
85073                 _wrap.selectAll('input')
85074                     .on('blur', change())
85075                     .on('change', change());
85076
85077                 _wrap.selectAll('input:not(.combobox-input)')
85078                     .on('input', change(true));
85079
85080                 if (_tags) { updateTags(_tags); }
85081             }
85082
85083
85084             function address(selection) {
85085                 _selection = selection;
85086
85087                 _wrap = selection.selectAll('.form-field-input-wrap')
85088                     .data([0]);
85089
85090                 _wrap = _wrap.enter()
85091                     .append('div')
85092                     .attr('class', 'form-field-input-wrap form-field-input-' + field.type)
85093                     .merge(_wrap);
85094
85095                 var extent = combinedEntityExtent();
85096
85097                 if (extent) {
85098                     var countryCode;
85099                     if (context.inIntro()) {
85100                         // localize the address format for the walkthrough
85101                         countryCode = _t('intro.graph.countrycode');
85102                     } else {
85103                         var center = extent.center();
85104                         countryCode = iso1A2Code(center);
85105                     }
85106                     if (countryCode) {
85107                         _countryCode = countryCode.toLowerCase();
85108                         updateForCountryCode();
85109                     }
85110                 }
85111             }
85112
85113
85114             function change(onInput) {
85115                 return function() {
85116                     var tags = {};
85117
85118                     _wrap.selectAll('input')
85119                         .each(function (subfield) {
85120                             var key = field.key + ':' + subfield.id;
85121
85122                             var value = this.value;
85123                             if (!onInput) { value = context.cleanTagValue(value); }
85124
85125                             // don't override multiple values with blank string
85126                             if (Array.isArray(_tags[key]) && !value) { return; }
85127
85128                             tags[key] = value || undefined;
85129                         });
85130
85131                     dispatch$1.call('change', this, tags, onInput);
85132                 };
85133             }
85134
85135             function updatePlaceholder(inputSelection) {
85136                 return inputSelection.attr('placeholder', function(subfield) {
85137                     if (_tags && Array.isArray(_tags[field.key + ':' + subfield.id])) {
85138                         return _t('inspector.multiple_values');
85139                     }
85140                     if (_countryCode) {
85141                         var localkey = subfield.id + '!' + _countryCode;
85142                         var tkey = addrField.strings.placeholders[localkey] ? localkey : subfield.id;
85143                         return addrField.t('placeholders.' + tkey);
85144                     }
85145                 });
85146             }
85147
85148
85149             function updateTags(tags) {
85150                 utilGetSetValue(_wrap.selectAll('input'), function (subfield) {
85151                         var val = tags[field.key + ':' + subfield.id];
85152                         return typeof val === 'string' ? val : '';
85153                     })
85154                     .attr('title', function(subfield) {
85155                         var val = tags[field.key + ':' + subfield.id];
85156                         return val && Array.isArray(val) && val.filter(Boolean).join('\n');
85157                     })
85158                     .classed('mixed', function(subfield) {
85159                         return Array.isArray(tags[field.key + ':' + subfield.id]);
85160                     })
85161                     .call(updatePlaceholder);
85162             }
85163
85164
85165             function combinedEntityExtent() {
85166                 return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph());
85167             }
85168
85169
85170             address.entityIDs = function(val) {
85171                 if (!arguments.length) { return _entityIDs; }
85172                 _entityIDs = val;
85173                 return address;
85174             };
85175
85176
85177             address.tags = function(tags) {
85178                 _tags = tags;
85179                 updateTags(tags);
85180             };
85181
85182
85183             address.focus = function() {
85184                 var node = _wrap.selectAll('input').node();
85185                 if (node) { node.focus(); }
85186             };
85187
85188
85189             return utilRebind(address, dispatch$1, 'on');
85190         }
85191
85192         function uiFieldCycleway(field, context) {
85193             var dispatch$1 = dispatch('change');
85194             var items = select(null);
85195             var wrap = select(null);
85196             var _tags;
85197
85198             function cycleway(selection) {
85199
85200                 function stripcolon(s) {
85201                     return s.replace(':', '');
85202                 }
85203
85204
85205                 wrap = selection.selectAll('.form-field-input-wrap')
85206                     .data([0]);
85207
85208                 wrap = wrap.enter()
85209                     .append('div')
85210                     .attr('class', 'form-field-input-wrap form-field-input-' + field.type)
85211                     .merge(wrap);
85212
85213
85214                 var div = wrap.selectAll('ul')
85215                     .data([0]);
85216
85217                 div = div.enter()
85218                     .append('ul')
85219                     .attr('class', 'rows')
85220                     .merge(div);
85221
85222                 var keys = ['cycleway:left', 'cycleway:right'];
85223
85224                 items = div.selectAll('li')
85225                     .data(keys);
85226
85227                 var enter = items.enter()
85228                     .append('li')
85229                     .attr('class', function(d) { return 'labeled-input preset-cycleway-' + stripcolon(d); });
85230
85231                 enter
85232                     .append('span')
85233                     .attr('class', 'label preset-label-cycleway')
85234                     .attr('for', function(d) { return 'preset-input-cycleway-' + stripcolon(d); })
85235                     .text(function(d) { return field.t('types.' + d); });
85236
85237                 enter
85238                     .append('div')
85239                     .attr('class', 'preset-input-cycleway-wrap')
85240                     .append('input')
85241                     .attr('type', 'text')
85242                     .attr('class', function(d) { return 'preset-input-cycleway preset-input-' + stripcolon(d); })
85243                     .call(utilNoAuto)
85244                     .each(function(d) {
85245                         select(this)
85246                             .call(uiCombobox(context, 'cycleway-' + stripcolon(d))
85247                                 .data(cycleway.options(d))
85248                             );
85249                     });
85250
85251                 items = items.merge(enter);
85252
85253                 // Update
85254                 wrap.selectAll('.preset-input-cycleway')
85255                     .on('change', change)
85256                     .on('blur', change);
85257             }
85258
85259
85260             function change(key) {
85261
85262                 var newValue = context.cleanTagValue(utilGetSetValue(select(this)));
85263
85264                 // don't override multiple values with blank string
85265                 if (!newValue && (Array.isArray(_tags.cycleway) || Array.isArray(_tags[key]))) { return; }
85266
85267                 if (newValue === 'none' || newValue === '') { newValue = undefined; }
85268
85269                 var otherKey = key === 'cycleway:left' ? 'cycleway:right' : 'cycleway:left';
85270                 var otherValue = typeof _tags.cycleway === 'string' ? _tags.cycleway : _tags[otherKey];
85271                 if (otherValue && Array.isArray(otherValue)) {
85272                     // we must always have an explicit value for comparison
85273                     otherValue = otherValue[0];
85274                 }
85275                 if (otherValue === 'none' || otherValue === '') { otherValue = undefined; }
85276
85277                 var tag = {};
85278
85279                 // If the left and right tags match, use the cycleway tag to tag both
85280                 // sides the same way
85281                 if (newValue === otherValue) {
85282                     tag = {
85283                         cycleway: newValue,
85284                         'cycleway:left': undefined,
85285                         'cycleway:right': undefined
85286                     };
85287                 } else {
85288                     // Always set both left and right as changing one can affect the other
85289                     tag = {
85290                         cycleway: undefined
85291                     };
85292                     tag[key] = newValue;
85293                     tag[otherKey] = otherValue;
85294                 }
85295
85296                 dispatch$1.call('change', this, tag);
85297             }
85298
85299
85300             cycleway.options = function() {
85301                 return Object.keys(field.strings.options).map(function(option) {
85302                     return {
85303                         title: field.t('options.' + option + '.description'),
85304                         value: option
85305                     };
85306                 });
85307             };
85308
85309
85310             cycleway.tags = function(tags) {
85311                 _tags = tags;
85312
85313                 // If cycleway is set, use that instead of individual values
85314                 var commonValue = typeof tags.cycleway === 'string' && tags.cycleway;
85315
85316                 utilGetSetValue(items.selectAll('.preset-input-cycleway'), function(d) {
85317                         if (commonValue) { return commonValue; }
85318                         return !tags.cycleway && typeof tags[d] === 'string' ? tags[d] : '';
85319                     })
85320                     .attr('title', function(d) {
85321                         if (Array.isArray(tags.cycleway) || Array.isArray(tags[d])) {
85322                             var vals = [];
85323                             if (Array.isArray(tags.cycleway)) {
85324                                 vals = vals.concat(tags.cycleway);
85325                             }
85326                             if (Array.isArray(tags[d])) {
85327                                 vals = vals.concat(tags[d]);
85328                             }
85329                             return vals.filter(Boolean).join('\n');
85330                         }
85331                         return null;
85332                     })
85333                     .attr('placeholder', function(d) {
85334                         if (Array.isArray(tags.cycleway) || Array.isArray(tags[d])) {
85335                             return _t('inspector.multiple_values');
85336                         }
85337                         return field.placeholder();
85338                     })
85339                     .classed('mixed', function(d) {
85340                         return Array.isArray(tags.cycleway) || Array.isArray(tags[d]);
85341                     });
85342             };
85343
85344
85345             cycleway.focus = function() {
85346                 var node = wrap.selectAll('input').node();
85347                 if (node) { node.focus(); }
85348             };
85349
85350
85351             return utilRebind(cycleway, dispatch$1, 'on');
85352         }
85353
85354         function uiFieldLanes(field, context) {
85355             var dispatch$1 = dispatch('change');
85356             var LANE_WIDTH = 40;
85357             var LANE_HEIGHT = 200;
85358             var _entityIDs = [];
85359
85360             function lanes(selection) {
85361                 var lanesData = context.entity(_entityIDs[0]).lanes();
85362
85363                 if (!context.container().select('.inspector-wrap.inspector-hidden').empty() || !selection.node().parentNode) {
85364                     selection.call(lanes.off);
85365                     return;
85366                 }
85367
85368                 var wrap = selection.selectAll('.form-field-input-wrap')
85369                     .data([0]);
85370
85371                 wrap = wrap.enter()
85372                     .append('div')
85373                     .attr('class', 'form-field-input-wrap form-field-input-' + field.type)
85374                     .merge(wrap);
85375
85376                 var surface =  wrap.selectAll('.surface')
85377                     .data([0]);
85378
85379                 var d = utilGetDimensions(wrap);
85380                 var freeSpace = d[0] - lanesData.lanes.length * LANE_WIDTH * 1.5 + LANE_WIDTH * 0.5;
85381
85382                 surface = surface.enter()
85383                     .append('svg')
85384                     .attr('width', d[0])
85385                     .attr('height', 300)
85386                     .attr('class', 'surface')
85387                     .merge(surface);
85388
85389
85390                 var lanesSelection = surface.selectAll('.lanes')
85391                     .data([0]);
85392
85393                 lanesSelection = lanesSelection.enter()
85394                     .append('g')
85395                     .attr('class', 'lanes')
85396                     .merge(lanesSelection);
85397
85398                 lanesSelection
85399                     .attr('transform', function () {
85400                         return 'translate(' + (freeSpace / 2) + ', 0)';
85401                     });
85402
85403
85404                 var lane = lanesSelection.selectAll('.lane')
85405                    .data(lanesData.lanes);
85406
85407                 lane.exit()
85408                     .remove();
85409
85410                 var enter = lane.enter()
85411                     .append('g')
85412                     .attr('class', 'lane');
85413
85414                 enter
85415                     .append('g')
85416                     .append('rect')
85417                     .attr('y', 50)
85418                     .attr('width', LANE_WIDTH)
85419                     .attr('height', LANE_HEIGHT);
85420
85421                 enter
85422                     .append('g')
85423                     .attr('class', 'forward')
85424                     .append('text')
85425                     .attr('y', 40)
85426                     .attr('x', 14)
85427                     .text('▲');
85428
85429                 enter
85430                     .append('g')
85431                     .attr('class', 'bothways')
85432                     .append('text')
85433                     .attr('y', 40)
85434                     .attr('x', 14)
85435                     .text('▲▼');
85436
85437                 enter
85438                     .append('g')
85439                     .attr('class', 'backward')
85440                     .append('text')
85441                     .attr('y', 40)
85442                     .attr('x', 14)
85443                     .text('▼');
85444
85445
85446                 lane = lane
85447                     .merge(enter);
85448
85449                 lane
85450                     .attr('transform', function(d) {
85451                         return 'translate(' + (LANE_WIDTH * d.index * 1.5) + ', 0)';
85452                     });
85453
85454                 lane.select('.forward')
85455                     .style('visibility', function(d) {
85456                         return d.direction === 'forward' ? 'visible' : 'hidden';
85457                     });
85458
85459                 lane.select('.bothways')
85460                     .style('visibility', function(d) {
85461                         return d.direction === 'bothways' ? 'visible' : 'hidden';
85462                     });
85463
85464                 lane.select('.backward')
85465                     .style('visibility', function(d) {
85466                         return d.direction === 'backward' ? 'visible' : 'hidden';
85467                     });
85468             }
85469
85470
85471             lanes.entityIDs = function(val) {
85472                 _entityIDs = val;
85473             };
85474
85475             lanes.tags = function() {};
85476             lanes.focus = function() {};
85477             lanes.off = function() {};
85478
85479             return utilRebind(lanes, dispatch$1, 'on');
85480         }
85481
85482         uiFieldLanes.supportsMultiselection = false;
85483
85484         var _languagesArray = [];
85485
85486
85487         function uiFieldLocalized(field, context) {
85488             var dispatch$1 = dispatch('change', 'input');
85489             var wikipedia = services.wikipedia;
85490             var input = select(null);
85491             var localizedInputs = select(null);
85492             var _countryCode;
85493             var _tags;
85494
85495
85496             // A concern here in switching to async data means that _languagesArray will not
85497             // be available the first time through, so things like the fetchers and
85498             // the language() function will not work immediately.
85499             _mainFileFetcher.get('languages')
85500                 .then(loadLanguagesArray)
85501                 .catch(function() { /* ignore */ });
85502
85503             var _territoryLanguages = {};
85504             _mainFileFetcher.get('territory_languages')
85505                 .then(function(d) { _territoryLanguages = d; })
85506                 .catch(function() { /* ignore */ });
85507
85508
85509             var allSuggestions = _mainPresetIndex.collection.filter(function(p) {
85510                 return p.suggestion === true;
85511             });
85512
85513             // reuse these combos
85514             var langCombo = uiCombobox(context, 'localized-lang')
85515                 .fetcher(fetchLanguages)
85516                 .minItems(0);
85517
85518             var brandCombo = uiCombobox(context, 'localized-brand')
85519                 .canAutocomplete(false)
85520                 .minItems(1);
85521
85522             var _selection = select(null);
85523             var _multilingual = [];
85524             var _buttonTip = uiTooltip()
85525                 .title(_t('translate.translate'))
85526                 .placement('left');
85527             var _wikiTitles;
85528             var _entityIDs = [];
85529
85530
85531             function loadLanguagesArray(dataLanguages) {
85532                 if (_languagesArray.length !== 0) { return; }
85533
85534                 // some conversion is needed to ensure correct OSM tags are used
85535                 var replacements = {
85536                     sr: 'sr-Cyrl',      // in OSM, `sr` implies Cyrillic
85537                     'sr-Cyrl': false    // `sr-Cyrl` isn't used in OSM
85538                 };
85539
85540                 for (var code in dataLanguages) {
85541                     if (replacements[code] === false) { continue; }
85542                     var metaCode = code;
85543                     if (replacements[code]) { metaCode = replacements[code]; }
85544
85545                     _languagesArray.push({
85546                         localName: _mainLocalizer.languageName(metaCode, { localOnly: true }),
85547                         nativeName: dataLanguages[metaCode].nativeName,
85548                         code: code,
85549                         label: _mainLocalizer.languageName(metaCode)
85550                     });
85551                 }
85552             }
85553
85554
85555             function calcLocked() {
85556
85557                 // only lock the Name field
85558                 var isLocked = field.id === 'name' &&
85559                     _entityIDs.length &&
85560                     // lock the field if any feature needs it
85561                     _entityIDs.some(function(entityID) {
85562
85563                         var entity = context.graph().hasEntity(entityID);
85564                         if (!entity) { return false; }
85565
85566                         var original = context.graph().base().entities[_entityIDs[0]];
85567                         var hasOriginalName = original && entity.tags.name && entity.tags.name === original.tags.name;
85568                         // if the name was already edited manually then allow further editing
85569                         if (!hasOriginalName) { return false; }
85570
85571                         // features linked to Wikidata are likely important and should be protected
85572                         if (entity.tags.wikidata) { return true; }
85573
85574                         // assume the name has already been confirmed if its source has been researched
85575                         if (entity.tags['name:etymology:wikidata']) { return true; }
85576
85577                         var preset = _mainPresetIndex.match(entity, context.graph());
85578                         var isSuggestion = preset && preset.suggestion;
85579                         var showsBrand = preset && preset.originalFields.filter(function(d) {
85580                             return d.id === 'brand';
85581                         }).length;
85582                         // protect standardized brand names
85583                         return isSuggestion && !showsBrand;
85584                     });
85585
85586                 field.locked(isLocked);
85587             }
85588
85589
85590             // update _multilingual, maintaining the existing order
85591             function calcMultilingual(tags) {
85592                 var existingLangsOrdered = _multilingual.map(function(item) {
85593                     return item.lang;
85594                 });
85595                 var existingLangs = new Set(existingLangsOrdered.filter(Boolean));
85596
85597                 for (var k in tags) {
85598                     var m = k.match(/^(.*):([a-zA-Z_-]+)$/);
85599                     if (m && m[1] === field.key && m[2]) {
85600                         var item = { lang: m[2], value: tags[k] };
85601                         if (existingLangs.has(item.lang)) {
85602                             // update the value
85603                             _multilingual[existingLangsOrdered.indexOf(item.lang)].value = item.value;
85604                             existingLangs.delete(item.lang);
85605                         } else {
85606                             _multilingual.push(item);
85607                         }
85608                     }
85609                 }
85610
85611                 _multilingual = _multilingual.filter(function(item) {
85612                     return !item.lang || !existingLangs.has(item.lang);
85613                 });
85614             }
85615
85616
85617             function localized(selection) {
85618                 _selection = selection;
85619                 calcLocked();
85620                 var isLocked = field.locked();
85621                 var singularEntity = _entityIDs.length === 1 && context.hasEntity(_entityIDs[0]);
85622                 var preset = singularEntity && _mainPresetIndex.match(singularEntity, context.graph());
85623
85624                 var wrap = selection.selectAll('.form-field-input-wrap')
85625                     .data([0]);
85626
85627                 // enter/update
85628                 wrap = wrap.enter()
85629                     .append('div')
85630                     .attr('class', 'form-field-input-wrap form-field-input-' + field.type)
85631                     .merge(wrap);
85632
85633                 input = wrap.selectAll('.localized-main')
85634                     .data([0]);
85635
85636                 // enter/update
85637                 input = input.enter()
85638                     .append('input')
85639                     .attr('type', 'text')
85640                     .attr('id', field.domId)
85641                     .attr('class', 'localized-main')
85642                     .call(utilNoAuto)
85643                     .merge(input);
85644
85645                 if (preset && field.id === 'name') {
85646                     var pTag = preset.id.split('/', 2);
85647                     var pKey = pTag[0];
85648                     var pValue = pTag[1];
85649
85650                     if (!preset.suggestion) {
85651                         // Not a suggestion preset - Add a suggestions dropdown if it makes sense to.
85652                         // This code attempts to determine if the matched preset is the
85653                         // kind of preset that even can benefit from name suggestions..
85654                         // - true = shops, cafes, hotels, etc. (also generic and fallback presets)
85655                         // - false = churches, parks, hospitals, etc. (things not in the index)
85656                         var isFallback = preset.isFallback();
85657                         var goodSuggestions = allSuggestions.filter(function(s) {
85658                             if (isFallback) { return true; }
85659                             var sTag = s.id.split('/', 2);
85660                             var sKey = sTag[0];
85661                             var sValue = sTag[1];
85662                             return pKey === sKey && (!pValue || pValue === sValue);
85663                         });
85664
85665                         // Show the suggestions.. If the user picks one, change the tags..
85666                         if (allSuggestions.length && goodSuggestions.length) {
85667                             input
85668                                 .on('blur.localized', checkBrandOnBlur)
85669                                 .call(brandCombo
85670                                     .fetcher(fetchBrandNames(preset, allSuggestions))
85671                                     .on('accept', acceptBrand)
85672                                     .on('cancel', cancelBrand)
85673                                 );
85674                         }
85675                     }
85676                 }
85677
85678                 input
85679                     .classed('disabled', !!isLocked)
85680                     .attr('readonly', isLocked || null)
85681                     .on('input', change(true))
85682                     .on('blur', change())
85683                     .on('change', change());
85684
85685
85686                 var translateButton = wrap.selectAll('.localized-add')
85687                     .data([0]);
85688
85689                 translateButton = translateButton.enter()
85690                     .append('button')
85691                     .attr('class', 'localized-add form-field-button')
85692                     .attr('tabindex', -1)
85693                     .call(svgIcon('#iD-icon-plus'))
85694                     .merge(translateButton);
85695
85696                 translateButton
85697                     .classed('disabled', !!isLocked)
85698                     .call(isLocked ? _buttonTip.destroy : _buttonTip)
85699                     .on('click', addNew);
85700
85701
85702                 if (_tags && !_multilingual.length) {
85703                     calcMultilingual(_tags);
85704                 }
85705
85706                 localizedInputs = selection.selectAll('.localized-multilingual')
85707                     .data([0]);
85708
85709                 localizedInputs = localizedInputs.enter()
85710                     .append('div')
85711                     .attr('class', 'localized-multilingual')
85712                     .merge(localizedInputs);
85713
85714                 localizedInputs
85715                     .call(renderMultilingual);
85716
85717                 localizedInputs.selectAll('button, input')
85718                     .classed('disabled', !!isLocked)
85719                     .attr('readonly', isLocked || null);
85720
85721
85722
85723                 // We are not guaranteed to get an `accept` or `cancel` when blurring the field.
85724                 // (This can happen if the user actives the combo, arrows down, and then clicks off to blur)
85725                 // So compare the current field value against the suggestions one last time.
85726                 function checkBrandOnBlur() {
85727                     var latest = _entityIDs.length === 1 && context.hasEntity(_entityIDs[0]);
85728                     if (!latest) { return; }   // deleting the entity blurred the field?
85729
85730                     var preset = _mainPresetIndex.match(latest, context.graph());
85731                     if (preset && preset.suggestion) { return; }   // already accepted
85732
85733                     // note: here we are testing against "decorated" names, i.e. 'Starbucks – Cafe'
85734                     var name = utilGetSetValue(input).trim();
85735                     var matched = allSuggestions.filter(function(s) { return name === s.name(); });
85736
85737                     if (matched.length === 1) {
85738                         acceptBrand({ suggestion: matched[0] });
85739                     } else {
85740                         cancelBrand();
85741                     }
85742                 }
85743
85744
85745                 function acceptBrand(d) {
85746
85747                     var entity = _entityIDs.length === 1 && context.hasEntity(_entityIDs[0]);
85748
85749                     if (!d || !entity) {
85750                         cancelBrand();
85751                         return;
85752                     }
85753
85754                     var tags = entity.tags;
85755                     var geometry = entity.geometry(context.graph());
85756                     var removed = preset.unsetTags(tags, geometry);
85757                     for (var k in tags) {
85758                         tags[k] = removed[k];  // set removed tags to `undefined`
85759                     }
85760                     tags = d.suggestion.setTags(tags, geometry);
85761                     utilGetSetValue(input, tags.name);
85762                     dispatch$1.call('change', this, tags);
85763                 }
85764
85765
85766                 // user hit escape, clean whatever preset name appears after the last ' – '
85767                 function cancelBrand() {
85768                     var name = utilGetSetValue(input);
85769                     var clean = cleanName(name);
85770                     if (clean !== name) {
85771                         utilGetSetValue(input, clean);
85772                         dispatch$1.call('change', this, { name: clean });
85773                     }
85774                 }
85775
85776                 // Remove whatever is after the last ' – '
85777                 // NOTE: split/join on en-dash, not a hypen (to avoid conflict with fr - nl names in Brussels etc)
85778                 function cleanName(name) {
85779                     var parts = name.split(' – ');
85780                     if (parts.length > 1) {
85781                         parts.pop();
85782                         name = parts.join(' – ');
85783                     }
85784                     return name;
85785                 }
85786
85787
85788                 function fetchBrandNames(preset, suggestions) {
85789                     var pTag = preset.id.split('/', 2);
85790                     var pKey = pTag[0];
85791                     var pValue = pTag[1];
85792
85793                     return function(value, callback) {
85794                         var results = [];
85795                         if (value && value.length > 2) {
85796                             for (var i = 0; i < suggestions.length; i++) {
85797                                 var s = suggestions[i];
85798
85799                                 // don't suggest brands from incompatible countries
85800                                 if (_countryCode && s.countryCodes &&
85801                                     s.countryCodes.indexOf(_countryCode) === -1) { continue; }
85802
85803                                 var sTag = s.id.split('/', 2);
85804                                 var sKey = sTag[0];
85805                                 var sValue = sTag[1];
85806                                 var name = s.name();
85807                                 var dist = utilEditDistance(value, name.substring(0, value.length));
85808                                 var matchesPreset = (pKey === sKey && (!pValue || pValue === sValue));
85809
85810                                 if (dist < 1 || (matchesPreset && dist < 3)) {
85811                                     var obj = {
85812                                         title: name,
85813                                         value: name,
85814                                         suggestion: s,
85815                                         dist: dist + (matchesPreset ? 0 : 1)  // penalize if not matched preset
85816                                     };
85817                                     results.push(obj);
85818                                 }
85819                             }
85820                             results.sort(function(a, b) { return a.dist - b.dist; });
85821                         }
85822                         results = results.slice(0, 10);
85823                         callback(results);
85824                     };
85825                 }
85826
85827
85828                 function addNew() {
85829                     event.preventDefault();
85830                     if (field.locked()) { return; }
85831
85832                     var defaultLang = _mainLocalizer.languageCode().toLowerCase();
85833                     var langExists = _multilingual.find(function(datum) { return datum.lang === defaultLang; });
85834                     var isLangEn = defaultLang.indexOf('en') > -1;
85835                     if (isLangEn || langExists) {
85836                         defaultLang = '';
85837                         langExists = _multilingual.find(function(datum) { return datum.lang === defaultLang; });
85838                     }
85839
85840                     if (!langExists) {
85841                         // prepend the value so it appears at the top
85842                         _multilingual.unshift({ lang: defaultLang, value: '' });
85843
85844                         localizedInputs
85845                             .call(renderMultilingual);
85846                     }
85847                 }
85848
85849
85850                 function change(onInput) {
85851                     return function() {
85852                         if (field.locked()) {
85853                             event.preventDefault();
85854                             return;
85855                         }
85856
85857                         var val = utilGetSetValue(select(this));
85858                         if (!onInput) { val = context.cleanTagValue(val); }
85859
85860                         // don't override multiple values with blank string
85861                         if (!val && Array.isArray(_tags[field.key])) { return; }
85862
85863                         var t = {};
85864
85865                         t[field.key] = val || undefined;
85866                         dispatch$1.call('change', this, t, onInput);
85867                     };
85868                 }
85869             }
85870
85871
85872             function key(lang) {
85873                 return field.key + ':' + lang;
85874             }
85875
85876
85877             function changeLang(d) {
85878                 var tags = {};
85879
85880                 // make sure unrecognized suffixes are lowercase - #7156
85881                 var lang = utilGetSetValue(select(this)).toLowerCase();
85882
85883                 var language = _languagesArray.find(function(d) {
85884                     return d.label.toLowerCase() === lang ||
85885                         (d.localName && d.localName.toLowerCase() === lang) ||
85886                         (d.nativeName && d.nativeName.toLowerCase() === lang);
85887                 });
85888                 if (language) { lang = language.code; }
85889
85890                 if (d.lang && d.lang !== lang) {
85891                     tags[key(d.lang)] = undefined;
85892                 }
85893
85894                 var newKey = lang && context.cleanTagKey(key(lang));
85895
85896                 var value = utilGetSetValue(select(this.parentNode).selectAll('.localized-value'));
85897
85898                 if (newKey && value) {
85899                     tags[newKey] = value;
85900                 } else if (newKey && _wikiTitles && _wikiTitles[d.lang]) {
85901                     tags[newKey] = _wikiTitles[d.lang];
85902                 }
85903
85904                 d.lang = lang;
85905                 dispatch$1.call('change', this, tags);
85906             }
85907
85908
85909             function changeValue(d) {
85910                 if (!d.lang) { return; }
85911                 var value = context.cleanTagValue(utilGetSetValue(select(this))) || undefined;
85912
85913                 // don't override multiple values with blank string
85914                 if (!value && Array.isArray(d.value)) { return; }
85915
85916                 var t = {};
85917                 t[key(d.lang)] = value;
85918                 d.value = value;
85919                 dispatch$1.call('change', this, t);
85920             }
85921
85922
85923             function fetchLanguages(value, cb) {
85924                 var v = value.toLowerCase();
85925
85926                 // show the user's language first
85927                 var langCodes = [_mainLocalizer.localeCode(), _mainLocalizer.languageCode()];
85928
85929                 if (_countryCode && _territoryLanguages[_countryCode]) {
85930                     langCodes = langCodes.concat(_territoryLanguages[_countryCode]);
85931                 }
85932
85933                 var langItems = [];
85934                 langCodes.forEach(function(code) {
85935                     var langItem = _languagesArray.find(function(item) {
85936                         return item.code === code;
85937                     });
85938                     if (langItem) { langItems.push(langItem); }
85939                 });
85940                 langItems = utilArrayUniq(langItems.concat(_languagesArray));
85941
85942                 cb(langItems.filter(function(d) {
85943                     return d.label.toLowerCase().indexOf(v) >= 0 ||
85944                         (d.localName && d.localName.toLowerCase().indexOf(v) >= 0) ||
85945                         (d.nativeName && d.nativeName.toLowerCase().indexOf(v) >= 0) ||
85946                         d.code.toLowerCase().indexOf(v) >= 0;
85947                 }).map(function(d) {
85948                     return { value: d.label };
85949                 }));
85950             }
85951
85952
85953             function renderMultilingual(selection) {
85954                 var entries = selection.selectAll('div.entry')
85955                     .data(_multilingual, function(d) { return d.lang; });
85956
85957                 entries.exit()
85958                     .style('top', '0')
85959                     .style('max-height', '240px')
85960                     .transition()
85961                     .duration(200)
85962                     .style('opacity', '0')
85963                     .style('max-height', '0px')
85964                     .remove();
85965
85966                 var entriesEnter = entries.enter()
85967                     .append('div')
85968                     .attr('class', 'entry')
85969                     .each(function(_, index) {
85970                         var wrap = select(this);
85971
85972                         var domId = utilUniqueDomId(index);
85973
85974                         var label = wrap
85975                             .append('label')
85976                             .attr('class', 'field-label')
85977                             .attr('for', domId);
85978
85979                         var text = label
85980                             .append('span')
85981                             .attr('class', 'label-text');
85982
85983                         text
85984                             .append('span')
85985                             .attr('class', 'label-textvalue')
85986                             .text(_t('translate.localized_translation_label'));
85987
85988                         text
85989                             .append('span')
85990                             .attr('class', 'label-textannotation');
85991
85992                         label
85993                             .append('button')
85994                             .attr('class', 'remove-icon-multilingual')
85995                             .on('click', function(d, index) {
85996                                 if (field.locked()) { return; }
85997                                 event.preventDefault();
85998
85999                                 if (!d.lang || !d.value) {
86000                                     _multilingual.splice(index, 1);
86001                                     renderMultilingual(selection);
86002                                 } else {
86003                                     // remove from entity tags
86004                                     var t = {};
86005                                     t[key(d.lang)] = undefined;
86006                                     dispatch$1.call('change', this, t);
86007                                 }
86008
86009                             })
86010                             .call(svgIcon('#iD-operation-delete'));
86011
86012                         wrap
86013                             .append('input')
86014                             .attr('class', 'localized-lang')
86015                             .attr('id', domId)
86016                             .attr('type', 'text')
86017                             .attr('placeholder', _t('translate.localized_translation_language'))
86018                             .on('blur', changeLang)
86019                             .on('change', changeLang)
86020                             .call(langCombo);
86021
86022                         wrap
86023                             .append('input')
86024                             .attr('type', 'text')
86025                             .attr('class', 'localized-value')
86026                             .on('blur', changeValue)
86027                             .on('change', changeValue);
86028                     });
86029
86030                 entriesEnter
86031                     .style('margin-top', '0px')
86032                     .style('max-height', '0px')
86033                     .style('opacity', '0')
86034                     .transition()
86035                     .duration(200)
86036                     .style('margin-top', '10px')
86037                     .style('max-height', '240px')
86038                     .style('opacity', '1')
86039                     .on('end', function() {
86040                         select(this)
86041                             .style('max-height', '')
86042                             .style('overflow', 'visible');
86043                     });
86044
86045                 entries = entries.merge(entriesEnter);
86046
86047                 entries.order();
86048
86049                 entries.classed('present', function(d) {
86050                     return d.lang && d.value;
86051                 });
86052
86053                 utilGetSetValue(entries.select('.localized-lang'), function(d) {
86054                     return _mainLocalizer.languageName(d.lang);
86055                 });
86056
86057                 utilGetSetValue(entries.select('.localized-value'), function(d) {
86058                         return typeof d.value === 'string' ? d.value : '';
86059                     })
86060                     .attr('title', function(d) {
86061                         return Array.isArray(d.value) ? d.value.filter(Boolean).join('\n') : null;
86062                     })
86063                     .attr('placeholder', function(d) {
86064                         return Array.isArray(d.value) ? _t('inspector.multiple_values') : _t('translate.localized_translation_name');
86065                     })
86066                     .classed('mixed', function(d) {
86067                         return Array.isArray(d.value);
86068                     });
86069             }
86070
86071
86072             localized.tags = function(tags) {
86073                 _tags = tags;
86074
86075                 // Fetch translations from wikipedia
86076                 if (typeof tags.wikipedia === 'string' && !_wikiTitles) {
86077                     _wikiTitles = {};
86078                     var wm = tags.wikipedia.match(/([^:]+):(.+)/);
86079                     if (wm && wm[0] && wm[1]) {
86080                         wikipedia.translations(wm[1], wm[2], function(err, d) {
86081                             if (err || !d) { return; }
86082                             _wikiTitles = d;
86083                         });
86084                     }
86085                 }
86086
86087                 var isMixed = Array.isArray(tags[field.key]);
86088
86089                 utilGetSetValue(input, typeof tags[field.key] === 'string' ? tags[field.key] : '')
86090                     .attr('title', isMixed ? tags[field.key].filter(Boolean).join('\n') : undefined)
86091                     .attr('placeholder', isMixed ? _t('inspector.multiple_values') : field.placeholder())
86092                     .classed('mixed', isMixed);
86093
86094                 calcMultilingual(tags);
86095
86096                 _selection
86097                     .call(localized);
86098             };
86099
86100
86101             localized.focus = function() {
86102                 input.node().focus();
86103             };
86104
86105
86106             localized.entityIDs = function(val) {
86107                 if (!arguments.length) { return _entityIDs; }
86108                 _entityIDs = val;
86109                 _multilingual = [];
86110                 loadCountryCode();
86111                 return localized;
86112             };
86113
86114             function loadCountryCode() {
86115                 var extent = combinedEntityExtent();
86116                 var countryCode = extent && iso1A2Code(extent.center());
86117                 _countryCode = countryCode && countryCode.toLowerCase();
86118             }
86119
86120             function combinedEntityExtent() {
86121                 return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph());
86122             }
86123
86124             return utilRebind(localized, dispatch$1, 'on');
86125         }
86126
86127         function uiFieldMaxspeed(field, context) {
86128             var dispatch$1 = dispatch('change');
86129             var unitInput = select(null);
86130             var input = select(null);
86131             var _entityIDs = [];
86132             var _tags;
86133             var _isImperial;
86134
86135             var speedCombo = uiCombobox(context, 'maxspeed');
86136             var unitCombo = uiCombobox(context, 'maxspeed-unit')
86137                     .data(['km/h', 'mph'].map(comboValues));
86138
86139             var metricValues = [20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120];
86140             var imperialValues = [5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80];
86141
86142
86143             function maxspeed(selection) {
86144
86145                 var wrap = selection.selectAll('.form-field-input-wrap')
86146                     .data([0]);
86147
86148                 wrap = wrap.enter()
86149                     .append('div')
86150                     .attr('class', 'form-field-input-wrap form-field-input-' + field.type)
86151                     .merge(wrap);
86152
86153
86154                 input = wrap.selectAll('input.maxspeed-number')
86155                     .data([0]);
86156
86157                 input = input.enter()
86158                     .append('input')
86159                     .attr('type', 'text')
86160                     .attr('class', 'maxspeed-number')
86161                     .attr('id', field.domId)
86162                     .call(utilNoAuto)
86163                     .call(speedCombo)
86164                     .merge(input);
86165
86166                 input
86167                     .on('change', change)
86168                     .on('blur', change);
86169
86170                 var loc = combinedEntityExtent().center();
86171                 _isImperial = roadSpeedUnit(loc) === 'mph';
86172
86173                 unitInput = wrap.selectAll('input.maxspeed-unit')
86174                     .data([0]);
86175
86176                 unitInput = unitInput.enter()
86177                     .append('input')
86178                     .attr('type', 'text')
86179                     .attr('class', 'maxspeed-unit')
86180                     .call(unitCombo)
86181                     .merge(unitInput);
86182
86183                 unitInput
86184                     .on('blur', changeUnits)
86185                     .on('change', changeUnits);
86186
86187
86188                 function changeUnits() {
86189                     _isImperial = utilGetSetValue(unitInput) === 'mph';
86190                     utilGetSetValue(unitInput, _isImperial ? 'mph' : 'km/h');
86191                     setUnitSuggestions();
86192                     change();
86193                 }
86194             }
86195
86196
86197             function setUnitSuggestions() {
86198                 speedCombo.data((_isImperial ? imperialValues : metricValues).map(comboValues));
86199                 utilGetSetValue(unitInput, _isImperial ? 'mph' : 'km/h');
86200             }
86201
86202
86203             function comboValues(d) {
86204                 return {
86205                     value: d.toString(),
86206                     title: d.toString()
86207                 };
86208             }
86209
86210
86211             function change() {
86212                 var tag = {};
86213                 var value = utilGetSetValue(input).trim();
86214
86215                 // don't override multiple values with blank string
86216                 if (!value && Array.isArray(_tags[field.key])) { return; }
86217
86218                 if (!value) {
86219                     tag[field.key] = undefined;
86220                 } else if (isNaN(value) || !_isImperial) {
86221                     tag[field.key] = context.cleanTagValue(value);
86222                 } else {
86223                     tag[field.key] = context.cleanTagValue(value + ' mph');
86224                 }
86225
86226                 dispatch$1.call('change', this, tag);
86227             }
86228
86229
86230             maxspeed.tags = function(tags) {
86231                 _tags = tags;
86232
86233                 var value = tags[field.key];
86234                 var isMixed = Array.isArray(value);
86235
86236                 if (!isMixed) {
86237                     if (value && value.indexOf('mph') >= 0) {
86238                         value = parseInt(value, 10).toString();
86239                         _isImperial = true;
86240                     } else if (value) {
86241                         _isImperial = false;
86242                     }
86243                 }
86244
86245                 setUnitSuggestions();
86246
86247                 utilGetSetValue(input, typeof value === 'string' ? value : '')
86248                     .attr('title', isMixed ? value.filter(Boolean).join('\n') : null)
86249                     .attr('placeholder', isMixed ? _t('inspector.multiple_values') : field.placeholder())
86250                     .classed('mixed', isMixed);
86251             };
86252
86253
86254             maxspeed.focus = function() {
86255                 input.node().focus();
86256             };
86257
86258
86259             maxspeed.entityIDs = function(val) {
86260                 _entityIDs = val;
86261             };
86262
86263
86264             function combinedEntityExtent() {
86265                 return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph());
86266             }
86267
86268
86269             return utilRebind(maxspeed, dispatch$1, 'on');
86270         }
86271
86272         function uiFieldRadio(field, context) {
86273             var dispatch$1 = dispatch('change');
86274             var placeholder = select(null);
86275             var wrap = select(null);
86276             var labels = select(null);
86277             var radios = select(null);
86278             var radioData = (field.options || (field.strings && field.strings.options && Object.keys(field.strings.options)) || field.keys).slice();  // shallow copy
86279             var typeField;
86280             var layerField;
86281             var _oldType = {};
86282             var _entityIDs = [];
86283
86284
86285             function selectedKey() {
86286                 var node = wrap.selectAll('.form-field-input-radio label.active input');
86287                 return !node.empty() && node.datum();
86288             }
86289
86290
86291             function radio(selection) {
86292                 selection.classed('preset-radio', true);
86293
86294                 wrap = selection.selectAll('.form-field-input-wrap')
86295                     .data([0]);
86296
86297                 var enter = wrap.enter()
86298                     .append('div')
86299                     .attr('class', 'form-field-input-wrap form-field-input-radio');
86300
86301                 enter
86302                     .append('span')
86303                     .attr('class', 'placeholder');
86304
86305                 wrap = wrap
86306                     .merge(enter);
86307
86308
86309                 placeholder = wrap.selectAll('.placeholder');
86310
86311                 labels = wrap.selectAll('label')
86312                     .data(radioData);
86313
86314                 enter = labels.enter()
86315                     .append('label');
86316
86317                 enter
86318                     .append('input')
86319                     .attr('type', 'radio')
86320                     .attr('name', field.id)
86321                     .attr('value', function(d) { return field.t('options.' + d, { 'default': d }); })
86322                     .attr('checked', false);
86323
86324                 enter
86325                     .append('span')
86326                     .text(function(d) { return field.t('options.' + d, { 'default': d }); });
86327
86328                 labels = labels
86329                     .merge(enter);
86330
86331                 radios = labels.selectAll('input')
86332                     .on('change', changeRadio);
86333
86334             }
86335
86336
86337             function structureExtras(selection, tags) {
86338                 var selected = selectedKey() || tags.layer !== undefined;
86339                 var type = _mainPresetIndex.field(selected);
86340                 var layer = _mainPresetIndex.field('layer');
86341                 var showLayer = (selected === 'bridge' || selected === 'tunnel' || tags.layer !== undefined);
86342
86343
86344                 var extrasWrap = selection.selectAll('.structure-extras-wrap')
86345                     .data(selected ? [0] : []);
86346
86347                 extrasWrap.exit()
86348                     .remove();
86349
86350                 extrasWrap = extrasWrap.enter()
86351                     .append('div')
86352                     .attr('class', 'structure-extras-wrap')
86353                     .merge(extrasWrap);
86354
86355                 var list = extrasWrap.selectAll('ul')
86356                     .data([0]);
86357
86358                 list = list.enter()
86359                     .append('ul')
86360                     .attr('class', 'rows')
86361                     .merge(list);
86362
86363
86364                 // Type
86365                 if (type) {
86366                     if (!typeField || typeField.id !== selected) {
86367                         typeField = uiField(context, type, _entityIDs, { wrap: false })
86368                             .on('change', changeType);
86369                     }
86370                     typeField.tags(tags);
86371                 } else {
86372                     typeField = null;
86373                 }
86374
86375                 var typeItem = list.selectAll('.structure-type-item')
86376                     .data(typeField ? [typeField] : [], function(d) { return d.id; });
86377
86378                 // Exit
86379                 typeItem.exit()
86380                     .remove();
86381
86382                 // Enter
86383                 var typeEnter = typeItem.enter()
86384                     .insert('li', ':first-child')
86385                     .attr('class', 'labeled-input structure-type-item');
86386
86387                 typeEnter
86388                     .append('span')
86389                     .attr('class', 'label structure-label-type')
86390                     .attr('for', 'preset-input-' + selected)
86391                     .text(_t('inspector.radio.structure.type'));
86392
86393                 typeEnter
86394                     .append('div')
86395                     .attr('class', 'structure-input-type-wrap');
86396
86397                 // Update
86398                 typeItem = typeItem
86399                     .merge(typeEnter);
86400
86401                 if (typeField) {
86402                     typeItem.selectAll('.structure-input-type-wrap')
86403                         .call(typeField.render);
86404                 }
86405
86406
86407                 // Layer
86408                 if (layer && showLayer) {
86409                     if (!layerField) {
86410                         layerField = uiField(context, layer, _entityIDs, { wrap: false })
86411                             .on('change', changeLayer);
86412                     }
86413                     layerField.tags(tags);
86414                     field.keys = utilArrayUnion(field.keys, ['layer']);
86415                 } else {
86416                     layerField = null;
86417                     field.keys = field.keys.filter(function(k) { return k !== 'layer'; });
86418                 }
86419
86420                 var layerItem = list.selectAll('.structure-layer-item')
86421                     .data(layerField ? [layerField] : []);
86422
86423                 // Exit
86424                 layerItem.exit()
86425                     .remove();
86426
86427                 // Enter
86428                 var layerEnter = layerItem.enter()
86429                     .append('li')
86430                     .attr('class', 'labeled-input structure-layer-item');
86431
86432                 layerEnter
86433                     .append('span')
86434                     .attr('class', 'label structure-label-layer')
86435                     .attr('for', 'preset-input-layer')
86436                     .text(_t('inspector.radio.structure.layer'));
86437
86438                 layerEnter
86439                     .append('div')
86440                     .attr('class', 'structure-input-layer-wrap');
86441
86442                 // Update
86443                 layerItem = layerItem
86444                     .merge(layerEnter);
86445
86446                 if (layerField) {
86447                     layerItem.selectAll('.structure-input-layer-wrap')
86448                         .call(layerField.render);
86449                 }
86450             }
86451
86452
86453             function changeType(t, onInput) {
86454                 var key = selectedKey();
86455                 if (!key) { return; }
86456
86457                 var val = t[key];
86458                 if (val !== 'no') {
86459                     _oldType[key] = val;
86460                 }
86461
86462                 if (field.type === 'structureRadio') {
86463                     // remove layer if it should not be set
86464                     if (val === 'no' ||
86465                         (key !== 'bridge' && key !== 'tunnel') ||
86466                         (key === 'tunnel' && val === 'building_passage')) {
86467                         t.layer = undefined;
86468                     }
86469                     // add layer if it should be set
86470                     if (t.layer === undefined) {
86471                         if (key === 'bridge' && val !== 'no') {
86472                             t.layer = '1';
86473                         }
86474                         if (key === 'tunnel' && val !== 'no' && val !== 'building_passage') {
86475                             t.layer = '-1';
86476                         }
86477                     }
86478                  }
86479
86480                 dispatch$1.call('change', this, t, onInput);
86481             }
86482
86483
86484             function changeLayer(t, onInput) {
86485                 if (t.layer === '0') {
86486                     t.layer = undefined;
86487                 }
86488                 dispatch$1.call('change', this, t, onInput);
86489             }
86490
86491
86492             function changeRadio() {
86493                 var t = {};
86494                 var activeKey;
86495
86496                 if (field.key) {
86497                     t[field.key] = undefined;
86498                 }
86499
86500                 radios.each(function(d) {
86501                     var active = select(this).property('checked');
86502                     if (active) { activeKey = d; }
86503
86504                     if (field.key) {
86505                         if (active) { t[field.key] = d; }
86506                     } else {
86507                         var val = _oldType[activeKey] || 'yes';
86508                         t[d] = active ? val : undefined;
86509                     }
86510                 });
86511
86512                 if (field.type === 'structureRadio') {
86513                     if (activeKey === 'bridge') {
86514                         t.layer = '1';
86515                     } else if (activeKey === 'tunnel' && t.tunnel !== 'building_passage') {
86516                         t.layer = '-1';
86517                     } else {
86518                         t.layer = undefined;
86519                     }
86520                 }
86521
86522                 dispatch$1.call('change', this, t);
86523             }
86524
86525
86526             radio.tags = function(tags) {
86527
86528                 radios.property('checked', function(d) {
86529                     if (field.key) {
86530                         return tags[field.key] === d;
86531                     }
86532                     return !!(typeof tags[d] === 'string' && tags[d].toLowerCase() !== 'no');
86533                 });
86534
86535                 function isMixed(d) {
86536                     if (field.key) {
86537                         return Array.isArray(tags[field.key]) && tags[field.key].includes(d);
86538                     }
86539                     return Array.isArray(tags[d]);
86540                 }
86541
86542                 labels
86543                     .classed('active', function(d) {
86544                         if (field.key) {
86545                             return (Array.isArray(tags[field.key]) && tags[field.key].includes(d))
86546                                 || tags[field.key] === d;
86547                         }
86548                         return Array.isArray(tags[d]) || !!(tags[d] && tags[d].toLowerCase() !== 'no');
86549                     })
86550                     .classed('mixed', isMixed)
86551                     .attr('title', function(d) {
86552                         return isMixed(d) ? _t('inspector.unshared_value_tooltip') : null;
86553                     });
86554
86555
86556                 var selection = radios.filter(function() { return this.checked; });
86557
86558                 if (selection.empty()) {
86559                     placeholder.text(_t('inspector.none'));
86560                 } else {
86561                     placeholder.text(selection.attr('value'));
86562                     _oldType[selection.datum()] = tags[selection.datum()];
86563                 }
86564
86565                 if (field.type === 'structureRadio') {
86566                     // For waterways without a tunnel tag, set 'culvert' as
86567                     // the _oldType to default to if the user picks 'tunnel'
86568                     if (!!tags.waterway && !_oldType.tunnel) {
86569                         _oldType.tunnel = 'culvert';
86570                     }
86571
86572                     wrap.call(structureExtras, tags);
86573                 }
86574             };
86575
86576
86577             radio.focus = function() {
86578                 radios.node().focus();
86579             };
86580
86581
86582             radio.entityIDs = function(val) {
86583                 if (!arguments.length) { return _entityIDs; }
86584                 _entityIDs = val;
86585                 _oldType = {};
86586                 return radio;
86587             };
86588
86589
86590             radio.isAllowed = function() {
86591                 return _entityIDs.length === 1;
86592             };
86593
86594
86595             return utilRebind(radio, dispatch$1, 'on');
86596         }
86597
86598         function uiFieldRestrictions(field, context) {
86599             var dispatch$1 = dispatch('change');
86600             var breathe = behaviorBreathe();
86601
86602             corePreferences('turn-restriction-via-way', null);                 // remove old key
86603             var storedViaWay = corePreferences('turn-restriction-via-way0');   // use new key #6922
86604             var storedDistance = corePreferences('turn-restriction-distance');
86605
86606             var _maxViaWay = storedViaWay !== null ? (+storedViaWay) : 0;
86607             var _maxDistance = storedDistance ? (+storedDistance) : 30;
86608             var _initialized = false;
86609             var _parent = select(null);       // the entire field
86610             var _container = select(null);    // just the map
86611             var _oldTurns;
86612             var _graph;
86613             var _vertexID;
86614             var _intersection;
86615             var _fromWayID;
86616
86617             var _lastXPos;
86618
86619
86620             function restrictions(selection) {
86621                 _parent = selection;
86622
86623                 // try to reuse the intersection, but always rebuild it if the graph has changed
86624                 if (_vertexID && (context.graph() !== _graph || !_intersection)) {
86625                     _graph = context.graph();
86626                     _intersection = osmIntersection(_graph, _vertexID, _maxDistance);
86627                 }
86628
86629                 // It's possible for there to be no actual intersection here.
86630                 // for example, a vertex of two `highway=path`
86631                 // In this case, hide the field.
86632                 var isOK = (
86633                     _intersection &&
86634                     _intersection.vertices.length &&           // has vertices
86635                     _intersection.vertices                     // has the vertex that the user selected
86636                         .filter(function(vertex) { return vertex.id === _vertexID; }).length &&
86637                     _intersection.ways.length > 2 &&           // has more than 2 ways
86638                     _intersection.ways                         // has more than 1 TO way
86639                         .filter(function(way) { return way.__to; }).length > 1
86640                 );
86641
86642                 // Also hide in the case where
86643                 select(selection.node().parentNode).classed('hide', !isOK);
86644
86645                 // if form field is hidden or has detached from dom, clean up.
86646                 if (!isOK ||
86647                     !context.container().select('.inspector-wrap.inspector-hidden').empty() ||
86648                     !selection.node().parentNode ||
86649                     !selection.node().parentNode.parentNode) {
86650                     selection.call(restrictions.off);
86651                     return;
86652                 }
86653
86654
86655                 var wrap = selection.selectAll('.form-field-input-wrap')
86656                     .data([0]);
86657
86658                 wrap = wrap.enter()
86659                     .append('div')
86660                     .attr('class', 'form-field-input-wrap form-field-input-' + field.type)
86661                     .merge(wrap);
86662
86663                 var container = wrap.selectAll('.restriction-container')
86664                     .data([0]);
86665
86666                 // enter
86667                 var containerEnter = container.enter()
86668                     .append('div')
86669                     .attr('class', 'restriction-container');
86670
86671                 containerEnter
86672                     .append('div')
86673                     .attr('class', 'restriction-help');
86674
86675                 // update
86676                 _container = containerEnter
86677                     .merge(container)
86678                     .call(renderViewer);
86679
86680                 var controls = wrap.selectAll('.restriction-controls')
86681                     .data([0]);
86682
86683                 // enter/update
86684                 controls.enter()
86685                     .append('div')
86686                     .attr('class', 'restriction-controls-container')
86687                     .append('div')
86688                     .attr('class', 'restriction-controls')
86689                     .merge(controls)
86690                     .call(renderControls);
86691             }
86692
86693
86694             function renderControls(selection) {
86695                 var distControl = selection.selectAll('.restriction-distance')
86696                     .data([0]);
86697
86698                 distControl.exit()
86699                     .remove();
86700
86701                 var distControlEnter = distControl.enter()
86702                     .append('div')
86703                     .attr('class', 'restriction-control restriction-distance');
86704
86705                 distControlEnter
86706                     .append('span')
86707                     .attr('class', 'restriction-control-label restriction-distance-label')
86708                     .text(_t('restriction.controls.distance') + ':');
86709
86710                 distControlEnter
86711                     .append('input')
86712                     .attr('class', 'restriction-distance-input')
86713                     .attr('type', 'range')
86714                     .attr('min', '20')
86715                     .attr('max', '50')
86716                     .attr('step', '5');
86717
86718                 distControlEnter
86719                     .append('span')
86720                     .attr('class', 'restriction-distance-text');
86721
86722                 // update
86723                 selection.selectAll('.restriction-distance-input')
86724                     .property('value', _maxDistance)
86725                     .on('input', function() {
86726                         var val = select(this).property('value');
86727                         _maxDistance = +val;
86728                         _intersection = null;
86729                         _container.selectAll('.layer-osm .layer-turns *').remove();
86730                         corePreferences('turn-restriction-distance', _maxDistance);
86731                         _parent.call(restrictions);
86732                     });
86733
86734                 selection.selectAll('.restriction-distance-text')
86735                     .text(displayMaxDistance(_maxDistance));
86736
86737
86738                 var viaControl = selection.selectAll('.restriction-via-way')
86739                     .data([0]);
86740
86741                 viaControl.exit()
86742                     .remove();
86743
86744                 var viaControlEnter = viaControl.enter()
86745                     .append('div')
86746                     .attr('class', 'restriction-control restriction-via-way');
86747
86748                 viaControlEnter
86749                     .append('span')
86750                     .attr('class', 'restriction-control-label restriction-via-way-label')
86751                     .text(_t('restriction.controls.via') + ':');
86752
86753                 viaControlEnter
86754                     .append('input')
86755                     .attr('class', 'restriction-via-way-input')
86756                     .attr('type', 'range')
86757                     .attr('min', '0')
86758                     .attr('max', '2')
86759                     .attr('step', '1');
86760
86761                 viaControlEnter
86762                     .append('span')
86763                     .attr('class', 'restriction-via-way-text');
86764
86765                 // update
86766                 selection.selectAll('.restriction-via-way-input')
86767                     .property('value', _maxViaWay)
86768                     .on('input', function() {
86769                         var val = select(this).property('value');
86770                         _maxViaWay = +val;
86771                         _container.selectAll('.layer-osm .layer-turns *').remove();
86772                         corePreferences('turn-restriction-via-way0', _maxViaWay);
86773                         _parent.call(restrictions);
86774                     });
86775
86776                 selection.selectAll('.restriction-via-way-text')
86777                     .text(displayMaxVia(_maxViaWay));
86778             }
86779
86780
86781             function renderViewer(selection) {
86782                 if (!_intersection) { return; }
86783
86784                 var vgraph = _intersection.graph;
86785                 var filter = utilFunctor(true);
86786                 var projection = geoRawMercator();
86787
86788                 // Reflow warning: `utilGetDimensions` calls `getBoundingClientRect`
86789                 // Instead of asking the restriction-container for its dimensions,
86790                 //  we can ask the .sidebar, which can have its dimensions cached.
86791                 // width: calc as sidebar - padding
86792                 // height: hardcoded (from `80_app.css`)
86793                 // var d = utilGetDimensions(selection);
86794                 var sdims = utilGetDimensions(context.container().select('.sidebar'));
86795                 var d = [ sdims[0] - 50, 370 ];
86796                 var c = geoVecScale(d, 0.5);
86797                 var z = 22;
86798
86799                 projection.scale(geoZoomToScale(z));
86800
86801                 // Calculate extent of all key vertices
86802                 var extent = geoExtent();
86803                 for (var i = 0; i < _intersection.vertices.length; i++) {
86804                     extent._extend(_intersection.vertices[i].extent());
86805                 }
86806
86807                 // If this is a large intersection, adjust zoom to fit extent
86808                 if (_intersection.vertices.length > 1) {
86809                     var padding = 180;   // in z22 pixels
86810                     var tl = projection([extent[0][0], extent[1][1]]);
86811                     var br = projection([extent[1][0], extent[0][1]]);
86812                     var hFactor = (br[0] - tl[0]) / (d[0] - padding);
86813                     var vFactor = (br[1] - tl[1]) / (d[1] - padding);
86814                     var hZoomDiff = Math.log(Math.abs(hFactor)) / Math.LN2;
86815                     var vZoomDiff = Math.log(Math.abs(vFactor)) / Math.LN2;
86816                     z = z - Math.max(hZoomDiff, vZoomDiff);
86817                     projection.scale(geoZoomToScale(z));
86818                 }
86819
86820                 var padTop = 35;   // reserve top space for hint text
86821                 var extentCenter = projection(extent.center());
86822                 extentCenter[1] = extentCenter[1] - padTop;
86823
86824                 projection
86825                     .translate(geoVecSubtract(c, extentCenter))
86826                     .clipExtent([[0, 0], d]);
86827
86828                 var drawLayers = svgLayers(projection, context).only(['osm','touch']).dimensions(d);
86829                 var drawVertices = svgVertices(projection, context);
86830                 var drawLines = svgLines(projection, context);
86831                 var drawTurns = svgTurns(projection, context);
86832
86833                 var firstTime = selection.selectAll('.surface').empty();
86834
86835                 selection
86836                     .call(drawLayers);
86837
86838                 var surface = selection.selectAll('.surface')
86839                     .classed('tr', true);
86840
86841                 if (firstTime) {
86842                     _initialized = true;
86843
86844                     surface
86845                         .call(breathe);
86846                 }
86847
86848                 // This can happen if we've lowered the detail while a FROM way
86849                 // is selected, and that way is no longer part of the intersection.
86850                 if (_fromWayID && !vgraph.hasEntity(_fromWayID)) {
86851                     _fromWayID = null;
86852                     _oldTurns = null;
86853                 }
86854
86855                 surface
86856                     .call(utilSetDimensions, d)
86857                     .call(drawVertices, vgraph, _intersection.vertices, filter, extent, z)
86858                     .call(drawLines, vgraph, _intersection.ways, filter)
86859                     .call(drawTurns, vgraph, _intersection.turns(_fromWayID, _maxViaWay));
86860
86861                 surface
86862                     .on('click.restrictions', click)
86863                     .on('mouseover.restrictions', mouseover);
86864
86865                 surface
86866                     .selectAll('.selected')
86867                     .classed('selected', false);
86868
86869                 surface
86870                     .selectAll('.related')
86871                     .classed('related', false);
86872
86873                 if (_fromWayID) {
86874                     var way = vgraph.entity(_fromWayID);
86875                     surface
86876                         .selectAll('.' + _fromWayID)
86877                         .classed('selected', true)
86878                         .classed('related', true);
86879                 }
86880
86881                 document.addEventListener('resizeWindow', function () {
86882                     utilSetDimensions(_container, null);
86883                     redraw(1);
86884                 }, false);
86885
86886                 updateHints(null);
86887
86888
86889                 function click() {
86890                     surface
86891                         .call(breathe.off)
86892                         .call(breathe);
86893
86894                     var datum = event.target.__data__;
86895                     var entity = datum && datum.properties && datum.properties.entity;
86896                     if (entity) {
86897                         datum = entity;
86898                     }
86899
86900                     if (datum instanceof osmWay && (datum.__from || datum.__via)) {
86901                         _fromWayID = datum.id;
86902                         _oldTurns = null;
86903                         redraw();
86904
86905                     } else if (datum instanceof osmTurn) {
86906                         var actions, extraActions, turns, i;
86907                         var restrictionType = osmInferRestriction(vgraph, datum, projection);
86908
86909                         if (datum.restrictionID && !datum.direct) {
86910                             return;
86911
86912                         } else if (datum.restrictionID && !datum.only) {    // NO -> ONLY
86913                             var seen = {};
86914                             var datumOnly = JSON.parse(JSON.stringify(datum));   // deep clone the datum
86915                             datumOnly.only = true;                               // but change this property
86916                             restrictionType = restrictionType.replace(/^no/, 'only');
86917
86918                             // Adding an ONLY restriction should destroy all other direct restrictions from the FROM towards the VIA.
86919                             // We will remember them in _oldTurns, and restore them if the user clicks again.
86920                             turns = _intersection.turns(_fromWayID, 2);
86921                             extraActions = [];
86922                             _oldTurns = [];
86923                             for (i = 0; i < turns.length; i++) {
86924                                 var turn = turns[i];
86925                                 if (seen[turn.restrictionID]) { continue; }  // avoid deleting the turn twice (#4968, #4928)
86926
86927                                 if (turn.direct && turn.path[1] === datum.path[1]) {
86928                                     seen[turns[i].restrictionID] = true;
86929                                     turn.restrictionType = osmInferRestriction(vgraph, turn, projection);
86930                                     _oldTurns.push(turn);
86931                                     extraActions.push(actionUnrestrictTurn(turn));
86932                                 }
86933                             }
86934
86935                             actions = _intersection.actions.concat(extraActions, [
86936                                 actionRestrictTurn(datumOnly, restrictionType),
86937                                 _t('operations.restriction.annotation.create')
86938                             ]);
86939
86940                         } else if (datum.restrictionID) {   // ONLY -> Allowed
86941                             // Restore whatever restrictions we might have destroyed by cycling thru the ONLY state.
86942                             // This relies on the assumption that the intersection was already split up when we
86943                             // performed the previous action (NO -> ONLY), so the IDs in _oldTurns shouldn't have changed.
86944                             turns = _oldTurns || [];
86945                             extraActions = [];
86946                             for (i = 0; i < turns.length; i++) {
86947                                 if (turns[i].key !== datum.key) {
86948                                     extraActions.push(actionRestrictTurn(turns[i], turns[i].restrictionType));
86949                                 }
86950                             }
86951                             _oldTurns = null;
86952
86953                             actions = _intersection.actions.concat(extraActions, [
86954                                 actionUnrestrictTurn(datum),
86955                                 _t('operations.restriction.annotation.delete')
86956                             ]);
86957
86958                         } else {    // Allowed -> NO
86959                             actions = _intersection.actions.concat([
86960                                 actionRestrictTurn(datum, restrictionType),
86961                                 _t('operations.restriction.annotation.create')
86962                             ]);
86963                         }
86964
86965                         context.perform.apply(context, actions);
86966
86967                         // At this point the datum will be changed, but will have same key..
86968                         // Refresh it and update the help..
86969                         var s = surface.selectAll('.' + datum.key);
86970                         datum = s.empty() ? null : s.datum();
86971                         updateHints(datum);
86972
86973                     } else {
86974                         _fromWayID = null;
86975                         _oldTurns = null;
86976                         redraw();
86977                     }
86978                 }
86979
86980
86981                 function mouseover() {
86982                     var datum = event.target.__data__;
86983                     updateHints(datum);
86984                 }
86985
86986                 _lastXPos = _lastXPos || sdims[0];
86987
86988                 function redraw(minChange) {
86989                     var xPos = -1;
86990
86991                     if (minChange) {
86992                         xPos = utilGetDimensions(context.container().select('.sidebar'))[0];
86993                     }
86994
86995                     if (!minChange || (minChange && Math.abs(xPos - _lastXPos) >= minChange)) {
86996                         if (context.hasEntity(_vertexID)) {
86997                             _lastXPos = xPos;
86998                             _container.call(renderViewer);
86999                         }
87000                     }
87001                 }
87002
87003
87004                 function highlightPathsFrom(wayID) {
87005                     surface.selectAll('.related')
87006                         .classed('related', false)
87007                         .classed('allow', false)
87008                         .classed('restrict', false)
87009                         .classed('only', false);
87010
87011                     surface.selectAll('.' + wayID)
87012                         .classed('related', true);
87013
87014                     if (wayID) {
87015                         var turns = _intersection.turns(wayID, _maxViaWay);
87016                         for (var i = 0; i < turns.length; i++) {
87017                             var turn = turns[i];
87018                             var ids = [turn.to.way];
87019                             var klass = (turn.no ? 'restrict' : (turn.only ? 'only' : 'allow'));
87020
87021                             if (turn.only || turns.length === 1) {
87022                                 if (turn.via.ways) {
87023                                     ids = ids.concat(turn.via.ways);
87024                                 }
87025                             } else if (turn.to.way === wayID) {
87026                                 continue;
87027                             }
87028
87029                             surface.selectAll(utilEntitySelector(ids))
87030                                 .classed('related', true)
87031                                 .classed('allow', (klass === 'allow'))
87032                                 .classed('restrict', (klass === 'restrict'))
87033                                 .classed('only', (klass === 'only'));
87034                         }
87035                     }
87036                 }
87037
87038
87039                 function updateHints(datum) {
87040                     var help = _container.selectAll('.restriction-help').html('');
87041
87042                     var placeholders = {};
87043                     ['from', 'via', 'to'].forEach(function(k) {
87044                         placeholders[k] = '<span class="qualifier">' + _t('restriction.help.' + k) + '</span>';
87045                     });
87046
87047                     var entity = datum && datum.properties && datum.properties.entity;
87048                     if (entity) {
87049                         datum = entity;
87050                     }
87051
87052                     if (_fromWayID) {
87053                         way = vgraph.entity(_fromWayID);
87054                         surface
87055                             .selectAll('.' + _fromWayID)
87056                             .classed('selected', true)
87057                             .classed('related', true);
87058                     }
87059
87060                     // Hovering a way
87061                     if (datum instanceof osmWay && datum.__from) {
87062                         way = datum;
87063
87064                         highlightPathsFrom(_fromWayID ? null : way.id);
87065                         surface.selectAll('.' + way.id)
87066                             .classed('related', true);
87067
87068                         var clickSelect = (!_fromWayID || _fromWayID !== way.id);
87069                         help
87070                             .append('div')      // "Click to select FROM {fromName}." / "FROM {fromName}"
87071                             .html(_t('restriction.help.' + (clickSelect ? 'select_from_name' : 'from_name'), {
87072                                 from: placeholders.from,
87073                                 fromName: displayName(way.id, vgraph)
87074                             }));
87075
87076
87077                     // Hovering a turn arrow
87078                     } else if (datum instanceof osmTurn) {
87079                         var restrictionType = osmInferRestriction(vgraph, datum, projection);
87080                         var turnType = restrictionType.replace(/^(only|no)\_/, '');
87081                         var indirect = (datum.direct === false ? _t('restriction.help.indirect') : '');
87082                         var klass, turnText, nextText;
87083
87084                         if (datum.no) {
87085                             klass = 'restrict';
87086                             turnText = _t('restriction.help.turn.no_' + turnType, { indirect: indirect });
87087                             nextText = _t('restriction.help.turn.only_' + turnType, { indirect: '' });
87088                         } else if (datum.only) {
87089                             klass = 'only';
87090                             turnText = _t('restriction.help.turn.only_' + turnType, { indirect: indirect });
87091                             nextText = _t('restriction.help.turn.allowed_' + turnType, { indirect: '' });
87092                         } else {
87093                             klass = 'allow';
87094                             turnText = _t('restriction.help.turn.allowed_' + turnType, { indirect: indirect });
87095                             nextText = _t('restriction.help.turn.no_' + turnType, { indirect: '' });
87096                         }
87097
87098                         help
87099                             .append('div')      // "NO Right Turn (indirect)"
87100                             .attr('class', 'qualifier ' + klass)
87101                             .text(turnText);
87102
87103                         help
87104                             .append('div')      // "FROM {fromName} TO {toName}"
87105                             .html(_t('restriction.help.from_name_to_name', {
87106                                 from: placeholders.from,
87107                                 fromName: displayName(datum.from.way, vgraph),
87108                                 to: placeholders.to,
87109                                 toName: displayName(datum.to.way, vgraph)
87110                             }));
87111
87112                         if (datum.via.ways && datum.via.ways.length) {
87113                             var names = [];
87114                             for (var i = 0; i < datum.via.ways.length; i++) {
87115                                 var prev = names[names.length - 1];
87116                                 var curr = displayName(datum.via.ways[i], vgraph);
87117                                 if (!prev || curr !== prev)   // collapse identical names
87118                                     { names.push(curr); }
87119                             }
87120
87121                             help
87122                                 .append('div')      // "VIA {viaNames}"
87123                                 .html(_t('restriction.help.via_names', {
87124                                     via: placeholders.via,
87125                                     viaNames: names.join(', ')
87126                                 }));
87127                         }
87128
87129                         if (!indirect) {
87130                             help
87131                                 .append('div')      // Click for "No Right Turn"
87132                                 .text(_t('restriction.help.toggle', { turn: nextText.trim() }));
87133                         }
87134
87135                         highlightPathsFrom(null);
87136                         var alongIDs = datum.path.slice();
87137                         surface.selectAll(utilEntitySelector(alongIDs))
87138                             .classed('related', true)
87139                             .classed('allow', (klass === 'allow'))
87140                             .classed('restrict', (klass === 'restrict'))
87141                             .classed('only', (klass === 'only'));
87142
87143
87144                     // Hovering empty surface
87145                     } else {
87146                         highlightPathsFrom(null);
87147                         if (_fromWayID) {
87148                             help
87149                                 .append('div')      // "FROM {fromName}"
87150                                 .html(_t('restriction.help.from_name', {
87151                                     from: placeholders.from,
87152                                     fromName: displayName(_fromWayID, vgraph)
87153                                 }));
87154
87155                         } else {
87156                             help
87157                                 .append('div')      // "Click to select a FROM segment."
87158                                 .html(_t('restriction.help.select_from', {
87159                                     from: placeholders.from
87160                                 }));
87161                         }
87162                     }
87163                 }
87164             }
87165
87166
87167             function displayMaxDistance(maxDist) {
87168                 var isImperial = !_mainLocalizer.usesMetric();
87169                 var opts;
87170
87171                 if (isImperial) {
87172                     var distToFeet = {   // imprecise conversion for prettier display
87173                         20: 70, 25: 85, 30: 100, 35: 115, 40: 130, 45: 145, 50: 160
87174                     }[maxDist];
87175                     opts = { distance: _t('units.feet', { quantity: distToFeet }) };
87176                 } else {
87177                     opts = { distance: _t('units.meters', { quantity: maxDist }) };
87178                 }
87179
87180                 return _t('restriction.controls.distance_up_to', opts);
87181             }
87182
87183
87184             function displayMaxVia(maxVia) {
87185                 return maxVia === 0 ? _t('restriction.controls.via_node_only')
87186                     : maxVia === 1 ? _t('restriction.controls.via_up_to_one')
87187                     : _t('restriction.controls.via_up_to_two');
87188             }
87189
87190
87191             function displayName(entityID, graph) {
87192                 var entity = graph.entity(entityID);
87193                 var name = utilDisplayName(entity) || '';
87194                 var matched = _mainPresetIndex.match(entity, graph);
87195                 var type = (matched && matched.name()) || utilDisplayType(entity.id);
87196                 return name || type;
87197             }
87198
87199
87200             restrictions.entityIDs = function(val) {
87201                 _intersection = null;
87202                 _fromWayID = null;
87203                 _oldTurns = null;
87204                 _vertexID = val[0];
87205             };
87206
87207
87208             restrictions.tags = function() {};
87209             restrictions.focus = function() {};
87210
87211
87212             restrictions.off = function(selection) {
87213                 if (!_initialized) { return; }
87214
87215                 selection.selectAll('.surface')
87216                     .call(breathe.off)
87217                     .on('click.restrictions', null)
87218                     .on('mouseover.restrictions', null);
87219
87220                 select(window)
87221                     .on('resize.restrictions', null);
87222             };
87223
87224
87225             return utilRebind(restrictions, dispatch$1, 'on');
87226         }
87227
87228         uiFieldRestrictions.supportsMultiselection = false;
87229
87230         function uiFieldTextarea(field, context) {
87231             var dispatch$1 = dispatch('change');
87232             var input = select(null);
87233             var _tags;
87234
87235
87236             function textarea(selection) {
87237                 var wrap = selection.selectAll('.form-field-input-wrap')
87238                     .data([0]);
87239
87240                 wrap = wrap.enter()
87241                     .append('div')
87242                     .attr('class', 'form-field-input-wrap form-field-input-' + field.type)
87243                     .merge(wrap);
87244
87245                 input = wrap.selectAll('textarea')
87246                     .data([0]);
87247
87248                 input = input.enter()
87249                     .append('textarea')
87250                     .attr('id', field.domId)
87251                     .call(utilNoAuto)
87252                     .on('input', change(true))
87253                     .on('blur', change())
87254                     .on('change', change())
87255                     .merge(input);
87256             }
87257
87258
87259             function change(onInput) {
87260                 return function() {
87261
87262                     var val = utilGetSetValue(input);
87263                     if (!onInput) { val = context.cleanTagValue(val); }
87264
87265                     // don't override multiple values with blank string
87266                     if (!val && Array.isArray(_tags[field.key])) { return; }
87267
87268                     var t = {};
87269                     t[field.key] = val || undefined;
87270                     dispatch$1.call('change', this, t, onInput);
87271                 };
87272             }
87273
87274
87275             textarea.tags = function(tags) {
87276                 _tags = tags;
87277
87278                 var isMixed = Array.isArray(tags[field.key]);
87279
87280                 utilGetSetValue(input, !isMixed && tags[field.key] ? tags[field.key] : '')
87281                     .attr('title', isMixed ? tags[field.key].filter(Boolean).join('\n') : undefined)
87282                     .attr('placeholder', isMixed ? _t('inspector.multiple_values') : (field.placeholder() || _t('inspector.unknown')))
87283                     .classed('mixed', isMixed);
87284             };
87285
87286
87287             textarea.focus = function() {
87288                 input.node().focus();
87289             };
87290
87291
87292             return utilRebind(textarea, dispatch$1, 'on');
87293         }
87294
87295         function uiFieldWikidata(field, context) {
87296             var wikidata = services.wikidata;
87297             var dispatch$1 = dispatch('change');
87298
87299             var _selection = select(null);
87300             var _searchInput = select(null);
87301             var _qid = null;
87302             var _wikidataEntity = null;
87303             var _wikiURL = '';
87304             var _entityIDs = [];
87305
87306             var _wikipediaKey = field.keys && field.keys.find(function(key) {
87307                     return key.includes('wikipedia');
87308                 }),
87309                 _hintKey = field.key === 'wikidata' ? 'name' : field.key.split(':')[0];
87310
87311             var combobox = uiCombobox(context, 'combo-' + field.safeid)
87312                 .caseSensitive(true)
87313                 .minItems(1);
87314
87315             function wiki(selection) {
87316
87317                 _selection = selection;
87318
87319                 var wrap = selection.selectAll('.form-field-input-wrap')
87320                     .data([0]);
87321
87322                 wrap = wrap.enter()
87323                     .append('div')
87324                     .attr('class', 'form-field-input-wrap form-field-input-' + field.type)
87325                     .merge(wrap);
87326
87327
87328                 var list = wrap.selectAll('ul')
87329                     .data([0]);
87330
87331                 list = list.enter()
87332                     .append('ul')
87333                     .attr('class', 'rows')
87334                     .merge(list);
87335
87336                 var searchRow = list.selectAll('li.wikidata-search')
87337                     .data([0]);
87338
87339                 var searchRowEnter = searchRow.enter()
87340                     .append('li')
87341                     .attr('class', 'wikidata-search');
87342
87343                 searchRowEnter
87344                     .append('input')
87345                     .attr('type', 'text')
87346                     .attr('id', field.domId)
87347                     .style('flex', '1')
87348                     .call(utilNoAuto)
87349                     .on('focus', function() {
87350                         var node = select(this).node();
87351                         node.setSelectionRange(0, node.value.length);
87352                     })
87353                     .on('blur', function() {
87354                         setLabelForEntity();
87355                     })
87356                     .call(combobox.fetcher(fetchWikidataItems));
87357
87358                 combobox.on('accept', function(d) {
87359                     _qid = d.id;
87360                     change();
87361                 }).on('cancel', function() {
87362                     setLabelForEntity();
87363                 });
87364
87365                 searchRowEnter
87366                     .append('button')
87367                     .attr('class', 'form-field-button wiki-link')
87368                     .attr('title', _t('icons.view_on', { domain: 'wikidata.org' }))
87369                     .attr('tabindex', -1)
87370                     .call(svgIcon('#iD-icon-out-link'))
87371                     .on('click', function() {
87372                         event.preventDefault();
87373                         if (_wikiURL) { window.open(_wikiURL, '_blank'); }
87374                     });
87375
87376                 searchRow = searchRow.merge(searchRowEnter);
87377
87378                 _searchInput = searchRow.select('input');
87379
87380                 var wikidataProperties = ['description', 'identifier'];
87381
87382                 var items = list.selectAll('li.labeled-input')
87383                     .data(wikidataProperties);
87384
87385                 // Enter
87386                 var enter = items.enter()
87387                     .append('li')
87388                     .attr('class', function(d) { return 'labeled-input preset-wikidata-' + d; });
87389
87390                 enter
87391                     .append('span')
87392                     .attr('class', 'label')
87393                     .text(function(d) { return _t('wikidata.' + d); });
87394
87395                 enter
87396                     .append('input')
87397                     .attr('type', 'text')
87398                     .call(utilNoAuto)
87399                     .classed('disabled', 'true')
87400                     .attr('readonly', 'true');
87401
87402                 enter
87403                     .append('button')
87404                     .attr('class', 'form-field-button')
87405                     .attr('title', _t('icons.copy'))
87406                     .attr('tabindex', -1)
87407                     .call(svgIcon('#iD-operation-copy'))
87408                     .on('click', function() {
87409                         event.preventDefault();
87410                         select(this.parentNode)
87411                             .select('input')
87412                             .node()
87413                             .select();
87414                         document.execCommand('copy');
87415                     });
87416
87417             }
87418
87419             function fetchWikidataItems(q, callback) {
87420
87421                 if (!q && _hintKey) {
87422                     // other tags may be good search terms
87423                     for (var i in _entityIDs) {
87424                         var entity = context.hasEntity(_entityIDs[i]);
87425                         if (entity.tags[_hintKey]) {
87426                             q = entity.tags[_hintKey];
87427                             break;
87428                         }
87429                     }
87430                 }
87431
87432                 wikidata.itemsForSearchQuery(q, function(err, data) {
87433                     if (err) { return; }
87434
87435                     for (var i in data) {
87436                         data[i].value = data[i].label + ' (' +  data[i].id + ')';
87437                         data[i].title = data[i].description;
87438                     }
87439
87440                     if (callback) { callback(data); }
87441                 });
87442             }
87443
87444
87445             function change() {
87446                 var syncTags = {};
87447                 syncTags[field.key] = _qid;
87448                 dispatch$1.call('change', this, syncTags);
87449
87450                 // attempt asynchronous update of wikidata tag..
87451                 var initGraph = context.graph();
87452                 var initEntityIDs = _entityIDs;
87453
87454                 wikidata.entityByQID(_qid, function(err, entity) {
87455                     if (err) { return; }
87456
87457                     // If graph has changed, we can't apply this update.
87458                     if (context.graph() !== initGraph) { return; }
87459
87460                     if (!entity.sitelinks) { return; }
87461
87462                     var langs = wikidata.languagesToQuery();
87463                     // use the label and description languages as fallbacks
87464                     ['labels', 'descriptions'].forEach(function(key) {
87465                         if (!entity[key]) { return; }
87466
87467                         var valueLangs = Object.keys(entity[key]);
87468                         if (valueLangs.length === 0) { return; }
87469                         var valueLang = valueLangs[0];
87470
87471                         if (langs.indexOf(valueLang) === -1) {
87472                             langs.push(valueLang);
87473                         }
87474                     });
87475
87476                     var newWikipediaValue;
87477
87478                     if (_wikipediaKey) {
87479                         var foundPreferred;
87480                         for (var i in langs) {
87481                             var lang = langs[i];
87482                             var siteID = lang.replace('-', '_') + 'wiki';
87483                             if (entity.sitelinks[siteID]) {
87484                                 foundPreferred = true;
87485                                 newWikipediaValue = lang + ':' + entity.sitelinks[siteID].title;
87486                                 // use the first match
87487                                 break;
87488                             }
87489                         }
87490
87491                         if (!foundPreferred) {
87492                             // No wikipedia sites available in the user's language or the fallback languages,
87493                             // default to any wikipedia sitelink
87494
87495                             var wikiSiteKeys = Object.keys(entity.sitelinks).filter(function(site) {
87496                                 return site.endsWith('wiki');
87497                             });
87498
87499                             if (wikiSiteKeys.length === 0) {
87500                                 // if no wikipedia pages are linked to this wikidata entity, delete that tag
87501                                 newWikipediaValue = null;
87502                             } else {
87503                                 var wikiLang = wikiSiteKeys[0].slice(0, -4).replace('_', '-');
87504                                 var wikiTitle = entity.sitelinks[wikiSiteKeys[0]].title;
87505                                 newWikipediaValue = wikiLang + ':' + wikiTitle;
87506                             }
87507                         }
87508                     }
87509
87510                     if (newWikipediaValue) {
87511                         newWikipediaValue = context.cleanTagValue(newWikipediaValue);
87512                     }
87513
87514                     if (typeof newWikipediaValue === 'undefined') { return; }
87515
87516                     var actions = initEntityIDs.map(function(entityID) {
87517                         var entity = context.hasEntity(entityID);
87518                         if (!entity) { return; }
87519
87520                         var currTags = Object.assign({}, entity.tags);  // shallow copy
87521                         if (newWikipediaValue === null) {
87522                             if (!currTags[_wikipediaKey]) { return; }
87523
87524                             delete currTags[_wikipediaKey];
87525                         } else {
87526                             currTags[_wikipediaKey] = newWikipediaValue;
87527                         }
87528
87529                         return actionChangeTags(entityID, currTags);
87530                     }).filter(Boolean);
87531
87532                     if (!actions.length) { return; }
87533
87534                     // Coalesce the update of wikidata tag into the previous tag change
87535                     context.overwrite(
87536                         function actionUpdateWikipediaTags(graph) {
87537                             actions.forEach(function(action) {
87538                                 graph = action(graph);
87539                             });
87540                             return graph;
87541                         },
87542                         context.history().undoAnnotation()
87543                     );
87544
87545                     // do not dispatch.call('change') here, because entity_editor
87546                     // changeTags() is not intended to be called asynchronously
87547                 });
87548             }
87549
87550             function setLabelForEntity() {
87551                 var label = '';
87552                 if (_wikidataEntity) {
87553                     label = entityPropertyForDisplay(_wikidataEntity, 'labels');
87554                     if (label.length === 0) {
87555                         label = _wikidataEntity.id.toString();
87556                     }
87557                 }
87558                 utilGetSetValue(_searchInput, label);
87559             }
87560
87561
87562             wiki.tags = function(tags) {
87563
87564                 var isMixed = Array.isArray(tags[field.key]);
87565                 _searchInput
87566                     .attr('title', isMixed ? tags[field.key].filter(Boolean).join('\n') : null)
87567                     .attr('placeholder', isMixed ? _t('inspector.multiple_values') : '')
87568                     .classed('mixed', isMixed);
87569
87570                 _qid = typeof tags[field.key] === 'string' && tags[field.key] || '';
87571
87572                 if (!/^Q[0-9]*$/.test(_qid)) {   // not a proper QID
87573                     unrecognized();
87574                     return;
87575                 }
87576
87577                 // QID value in correct format
87578                 _wikiURL = 'https://wikidata.org/wiki/' + _qid;
87579                 wikidata.entityByQID(_qid, function(err, entity) {
87580                     if (err) {
87581                         unrecognized();
87582                         return;
87583                     }
87584                     _wikidataEntity = entity;
87585
87586                     setLabelForEntity();
87587
87588                     var description = entityPropertyForDisplay(entity, 'descriptions');
87589
87590                     _selection.select('button.wiki-link')
87591                         .classed('disabled', false);
87592
87593                     _selection.select('.preset-wikidata-description')
87594                         .style('display', function(){
87595                             return description.length > 0 ? 'flex' : 'none';
87596                         })
87597                         .select('input')
87598                         .attr('value', description);
87599
87600                     _selection.select('.preset-wikidata-identifier')
87601                         .style('display', function(){
87602                             return entity.id ? 'flex' : 'none';
87603                         })
87604                         .select('input')
87605                         .attr('value', entity.id);
87606                 });
87607
87608
87609                 // not a proper QID
87610                 function unrecognized() {
87611                     _wikidataEntity = null;
87612                     setLabelForEntity();
87613
87614                     _selection.select('.preset-wikidata-description')
87615                         .style('display', 'none');
87616                     _selection.select('.preset-wikidata-identifier')
87617                         .style('display', 'none');
87618
87619                     _selection.select('button.wiki-link')
87620                         .classed('disabled', true);
87621
87622                     if (_qid && _qid !== '') {
87623                         _wikiURL = 'https://wikidata.org/wiki/Special:Search?search=' + _qid;
87624                     } else {
87625                         _wikiURL = '';
87626                     }
87627                 }
87628             };
87629
87630             function entityPropertyForDisplay(wikidataEntity, propKey) {
87631                 if (!wikidataEntity[propKey]) { return ''; }
87632                 var propObj = wikidataEntity[propKey];
87633                 var langKeys = Object.keys(propObj);
87634                 if (langKeys.length === 0) { return ''; }
87635                 // sorted by priority, since we want to show the user's language first if possible
87636                 var langs = wikidata.languagesToQuery();
87637                 for (var i in langs) {
87638                     var lang = langs[i];
87639                     var valueObj = propObj[lang];
87640                     if (valueObj && valueObj.value && valueObj.value.length > 0) { return valueObj.value; }
87641                 }
87642                 // default to any available value
87643                 return propObj[langKeys[0]].value;
87644             }
87645
87646
87647             wiki.entityIDs = function(val) {
87648                 if (!arguments.length) { return _entityIDs; }
87649                 _entityIDs = val;
87650                 return wiki;
87651             };
87652
87653
87654             wiki.focus = function() {
87655                 _searchInput.node().focus();
87656             };
87657
87658
87659             return utilRebind(wiki, dispatch$1, 'on');
87660         }
87661
87662         function uiFieldWikipedia(field, context) {
87663           var arguments$1 = arguments;
87664
87665           var dispatch$1 = dispatch('change');
87666           var wikipedia = services.wikipedia;
87667           var wikidata = services.wikidata;
87668           var _langInput = select(null);
87669           var _titleInput = select(null);
87670           var _wikiURL = '';
87671           var _entityIDs;
87672           var _tags;
87673
87674           var _dataWikipedia = [];
87675           _mainFileFetcher.get('wmf_sitematrix')
87676             .then(function (d) {
87677               _dataWikipedia = d;
87678               if (_tags) { updateForTags(_tags); }
87679             })
87680             .catch(function () { /* ignore */ });
87681
87682
87683           var langCombo = uiCombobox(context, 'wikipedia-lang')
87684             .fetcher(function (value, callback) {
87685               var v = value.toLowerCase();
87686               callback(_dataWikipedia
87687                 .filter(function (d) {
87688                   return d[0].toLowerCase().indexOf(v) >= 0 ||
87689                     d[1].toLowerCase().indexOf(v) >= 0 ||
87690                     d[2].toLowerCase().indexOf(v) >= 0;
87691                 })
87692                 .map(function (d) { return ({ value: d[1] }); })
87693               );
87694             });
87695
87696           var titleCombo = uiCombobox(context, 'wikipedia-title')
87697             .fetcher(function (value, callback) {
87698               if (!value) {
87699                 value = '';
87700                 for (var i in _entityIDs) {
87701                   var entity = context.hasEntity(_entityIDs[i]);
87702                   if (entity.tags.name) {
87703                     value = entity.tags.name;
87704                     break;
87705                   }
87706                 }
87707               }
87708               var searchfn = value.length > 7 ? wikipedia.search : wikipedia.suggestions;
87709               searchfn(language()[2], value, function (query, data) {
87710                 callback( data.map(function (d) { return ({ value: d }); }) );
87711               });
87712             });
87713
87714
87715           function wiki(selection) {
87716             var wrap = selection.selectAll('.form-field-input-wrap')
87717               .data([0]);
87718
87719             wrap = wrap.enter()
87720               .append('div')
87721               .attr('class', ("form-field-input-wrap form-field-input-" + (field.type)))
87722               .merge(wrap);
87723
87724
87725             var langContainer = wrap.selectAll('.wiki-lang-container')
87726               .data([0]);
87727
87728             langContainer = langContainer.enter()
87729               .append('div')
87730               .attr('class', 'wiki-lang-container')
87731               .merge(langContainer);
87732
87733
87734             _langInput = langContainer.selectAll('input.wiki-lang')
87735               .data([0]);
87736
87737             _langInput = _langInput.enter()
87738               .append('input')
87739               .attr('type', 'text')
87740               .attr('class', 'wiki-lang')
87741               .attr('placeholder', _t('translate.localized_translation_language'))
87742               .call(utilNoAuto)
87743               .call(langCombo)
87744               .merge(_langInput);
87745
87746             utilGetSetValue(_langInput, language()[1]);
87747
87748             _langInput
87749               .on('blur', changeLang)
87750               .on('change', changeLang);
87751
87752
87753             var titleContainer = wrap.selectAll('.wiki-title-container')
87754               .data([0]);
87755
87756             titleContainer = titleContainer.enter()
87757               .append('div')
87758               .attr('class', 'wiki-title-container')
87759               .merge(titleContainer);
87760
87761             _titleInput = titleContainer.selectAll('input.wiki-title')
87762               .data([0]);
87763
87764             _titleInput = _titleInput.enter()
87765               .append('input')
87766               .attr('type', 'text')
87767               .attr('class', 'wiki-title')
87768               .attr('id', field.domId)
87769               .call(utilNoAuto)
87770               .call(titleCombo)
87771               .merge(_titleInput);
87772
87773             _titleInput
87774               .on('blur', blur)
87775               .on('change', change);
87776
87777
87778             var link = titleContainer.selectAll('.wiki-link')
87779               .data([0]);
87780
87781             link = link.enter()
87782               .append('button')
87783               .attr('class', 'form-field-button wiki-link')
87784               .attr('tabindex', -1)
87785               .attr('title', _t('icons.view_on', { domain: 'wikipedia.org' }))
87786               .call(svgIcon('#iD-icon-out-link'))
87787               .merge(link);
87788
87789             link
87790               .on('click', function () {
87791                 event.preventDefault();
87792                 if (_wikiURL) { window.open(_wikiURL, '_blank'); }
87793               });
87794           }
87795
87796
87797           function language() {
87798             var value = utilGetSetValue(_langInput).toLowerCase();
87799             var locale = _mainLocalizer.localeCode().toLowerCase();
87800             var localeLanguage;
87801             return _dataWikipedia.find(function (d) {
87802               if (d[2] === locale) { localeLanguage = d; }
87803               return d[0].toLowerCase() === value || d[1].toLowerCase() === value || d[2] === value;
87804             }) || localeLanguage || ['English', 'English', 'en'];
87805           }
87806
87807
87808           function changeLang() {
87809             utilGetSetValue(_langInput, language()[1]);
87810             change(true);
87811           }
87812
87813
87814           function blur() {
87815             change(true);
87816           }
87817
87818
87819           function change(skipWikidata) {
87820             var value = utilGetSetValue(_titleInput);
87821             var m = value.match(/https?:\/\/([-a-z]+)\.wikipedia\.org\/(?:wiki|\1-[-a-z]+)\/([^#]+)(?:#(.+))?/);
87822             var l = m && _dataWikipedia.find(function (d) { return m[1] === d[2]; });
87823             var syncTags = {};
87824
87825             if (l) {
87826               // Normalize title http://www.mediawiki.org/wiki/API:Query#Title_normalization
87827               value = decodeURIComponent(m[2]).replace(/_/g, ' ');
87828               if (m[3]) {
87829                 var anchor;
87830                 // try {
87831                 // leave this out for now - #6232
87832                   // Best-effort `anchordecode:` implementation
87833                   // anchor = decodeURIComponent(m[3].replace(/\.([0-9A-F]{2})/g, '%$1'));
87834                 // } catch (e) {
87835                 anchor = decodeURIComponent(m[3]);
87836                 // }
87837                 value += '#' + anchor.replace(/_/g, ' ');
87838               }
87839               value = value.slice(0, 1).toUpperCase() + value.slice(1);
87840               utilGetSetValue(_langInput, l[1]);
87841               utilGetSetValue(_titleInput, value);
87842             }
87843
87844             if (value) {
87845               syncTags.wikipedia = context.cleanTagValue(language()[2] + ':' + value);
87846             } else {
87847               syncTags.wikipedia = undefined;
87848             }
87849
87850             dispatch$1.call('change', this, syncTags);
87851
87852
87853             if (skipWikidata || !value || !language()[2]) { return; }
87854
87855             // attempt asynchronous update of wikidata tag..
87856             var initGraph = context.graph();
87857             var initEntityIDs = _entityIDs;
87858
87859             wikidata.itemsByTitle(language()[2], value, function (err, data) {
87860               if (err || !data || !Object.keys(data).length) { return; }
87861
87862               // If graph has changed, we can't apply this update.
87863               if (context.graph() !== initGraph) { return; }
87864
87865               var qids = Object.keys(data);
87866               var value = qids && qids.find(function (id) { return id.match(/^Q\d+$/); });
87867
87868               var actions = initEntityIDs.map(function (entityID) {
87869                 var entity = context.entity(entityID).tags;
87870                 var currTags = Object.assign({}, entity);  // shallow copy
87871                 if (currTags.wikidata !== value) {
87872                     currTags.wikidata = value;
87873                     return actionChangeTags(entityID, currTags);
87874                 }
87875               }).filter(Boolean);
87876
87877               if (!actions.length) { return; }
87878
87879               // Coalesce the update of wikidata tag into the previous tag change
87880               context.overwrite(
87881                 function actionUpdateWikidataTags(graph) {
87882                   actions.forEach(function(action) {
87883                     graph = action(graph);
87884                   });
87885                   return graph;
87886                 },
87887                 context.history().undoAnnotation()
87888               );
87889
87890               // do not dispatch.call('change') here, because entity_editor
87891               // changeTags() is not intended to be called asynchronously
87892             });
87893           }
87894
87895
87896           wiki.tags = function (tags) {
87897             _tags = tags;
87898             updateForTags(tags);
87899           };
87900
87901           function updateForTags(tags) {
87902
87903             var value = typeof tags[field.key] === 'string' ? tags[field.key] : '';
87904             var m = value.match(/([^:]+):([^#]+)(?:#(.+))?/);
87905             var l = m && _dataWikipedia.find(function (d) { return m[1] === d[2]; });
87906             var anchor = m && m[3];
87907
87908             // value in correct format
87909             if (l) {
87910               utilGetSetValue(_langInput, l[1]);
87911               utilGetSetValue(_titleInput, m[2] + (anchor ? ('#' + anchor) : ''));
87912               if (anchor) {
87913                 try {
87914                   // Best-effort `anchorencode:` implementation
87915                   anchor = encodeURIComponent(anchor.replace(/ /g, '_')).replace(/%/g, '.');
87916                 } catch (e) {
87917                   anchor = anchor.replace(/ /g, '_');
87918                 }
87919               }
87920               _wikiURL = 'https://' + m[1] + '.wikipedia.org/wiki/' +
87921                 m[2].replace(/ /g, '_') + (anchor ? ('#' + anchor) : '');
87922
87923             // unrecognized value format
87924             } else {
87925               utilGetSetValue(_titleInput, value);
87926               if (value && value !== '') {
87927                 utilGetSetValue(_langInput, '');
87928                 _wikiURL = "https://en.wikipedia.org/wiki/Special:Search?search=" + value;
87929               } else {
87930                 _wikiURL = '';
87931               }
87932             }
87933           }
87934
87935
87936           wiki.entityIDs = function (val) {
87937             if (!arguments$1.length) { return _entityIDs; }
87938             _entityIDs = val;
87939             return wiki;
87940           };
87941
87942
87943           wiki.focus = function () {
87944             _titleInput.node().focus();
87945           };
87946
87947
87948           return utilRebind(wiki, dispatch$1, 'on');
87949         }
87950
87951         uiFieldWikipedia.supportsMultiselection = false;
87952
87953         var uiFields = {
87954             access: uiFieldAccess,
87955             address: uiFieldAddress,
87956             check: uiFieldCheck,
87957             combo: uiFieldCombo,
87958             cycleway: uiFieldCycleway,
87959             defaultCheck: uiFieldCheck,
87960             email: uiFieldText,
87961             identifier: uiFieldText,
87962             lanes: uiFieldLanes,
87963             localized: uiFieldLocalized,
87964             maxspeed: uiFieldMaxspeed,
87965             multiCombo: uiFieldCombo,
87966             networkCombo: uiFieldCombo,
87967             number: uiFieldText,
87968             onewayCheck: uiFieldCheck,
87969             radio: uiFieldRadio,
87970             restrictions: uiFieldRestrictions,
87971             semiCombo: uiFieldCombo,
87972             structureRadio: uiFieldRadio,
87973             tel: uiFieldText,
87974             text: uiFieldText,
87975             textarea: uiFieldTextarea,
87976             typeCombo: uiFieldCombo,
87977             url: uiFieldText,
87978             wikidata: uiFieldWikidata,
87979             wikipedia: uiFieldWikipedia
87980         };
87981
87982         function uiField(context, presetField, entityIDs, options) {
87983             options = Object.assign({
87984                 show: true,
87985                 wrap: true,
87986                 remove: true,
87987                 revert: true,
87988                 info: true
87989             }, options);
87990
87991             var dispatch$1 = dispatch('change', 'revert');
87992             var field = Object.assign({}, presetField);   // shallow copy
87993             field.domId = utilUniqueDomId('form-field-' + field.safeid);
87994             var _show = options.show;
87995             var _state = '';
87996             var _tags = {};
87997
87998             var _locked = false;
87999             var _lockedTip = uiTooltip()
88000                 .title(_t('inspector.lock.suggestion', { label: field.label }))
88001                 .placement('bottom');
88002
88003
88004             field.keys = field.keys || [field.key];
88005
88006             // only create the fields that are actually being shown
88007             if (_show && !field.impl) {
88008                 createField();
88009             }
88010
88011             // Creates the field.. This is done lazily,
88012             // once we know that the field will be shown.
88013             function createField() {
88014                 field.impl = uiFields[field.type](field, context)
88015                     .on('change', function(t, onInput) {
88016                         dispatch$1.call('change', field, t, onInput);
88017                     });
88018
88019                 if (entityIDs) {
88020                     field.entityIDs = entityIDs;
88021                     // if this field cares about the entities, pass them along
88022                     if (field.impl.entityIDs) {
88023                         field.impl.entityIDs(entityIDs);
88024                     }
88025                 }
88026             }
88027
88028
88029             function isModified() {
88030                 if (!entityIDs || !entityIDs.length) { return false; }
88031                 return entityIDs.some(function(entityID) {
88032                     var original = context.graph().base().entities[entityID];
88033                     var latest = context.graph().entity(entityID);
88034                     return field.keys.some(function(key) {
88035                         return original ? latest.tags[key] !== original.tags[key] : latest.tags[key];
88036                     });
88037                 });
88038             }
88039
88040
88041             function tagsContainFieldKey() {
88042                 return field.keys.some(function(key) {
88043                     if (field.type === 'multiCombo') {
88044                         for (var tagKey in _tags) {
88045                             if (tagKey.indexOf(key) === 0) {
88046                                 return true;
88047                             }
88048                         }
88049                         return false;
88050                     }
88051                     return _tags[key] !== undefined;
88052                 });
88053             }
88054
88055
88056             function revert(d) {
88057                 event.stopPropagation();
88058                 event.preventDefault();
88059                 if (!entityIDs || _locked) { return; }
88060
88061                 dispatch$1.call('revert', d, d.keys);
88062             }
88063
88064
88065             function remove(d) {
88066                 event.stopPropagation();
88067                 event.preventDefault();
88068                 if (_locked) { return; }
88069
88070                 var t = {};
88071                 d.keys.forEach(function(key) {
88072                     t[key] = undefined;
88073                 });
88074
88075                 dispatch$1.call('change', d, t);
88076             }
88077
88078
88079             field.render = function(selection) {
88080                 var container = selection.selectAll('.form-field')
88081                     .data([field]);
88082
88083                 // Enter
88084                 var enter = container.enter()
88085                     .append('div')
88086                     .attr('class', function(d) { return 'form-field form-field-' + d.safeid; })
88087                     .classed('nowrap', !options.wrap);
88088
88089                 if (options.wrap) {
88090                     var labelEnter = enter
88091                         .append('label')
88092                         .attr('class', 'field-label')
88093                         .attr('for', function(d) { return d.domId; });
88094
88095                     var textEnter = labelEnter
88096                         .append('span')
88097                         .attr('class', 'label-text');
88098
88099                     textEnter
88100                         .append('span')
88101                         .attr('class', 'label-textvalue')
88102                         .text(function(d) { return d.label(); });
88103
88104                     textEnter
88105                         .append('span')
88106                         .attr('class', 'label-textannotation');
88107
88108                     if (options.remove) {
88109                         labelEnter
88110                             .append('button')
88111                             .attr('class', 'remove-icon')
88112                             .attr('title', _t('icons.remove'))
88113                             .attr('tabindex', -1)
88114                             .call(svgIcon('#iD-operation-delete'));
88115                     }
88116
88117                     if (options.revert) {
88118                         labelEnter
88119                             .append('button')
88120                             .attr('class', 'modified-icon')
88121                             .attr('title', _t('icons.undo'))
88122                             .attr('tabindex', -1)
88123                             .call(svgIcon((_mainLocalizer.textDirection() === 'rtl') ? '#iD-icon-redo' : '#iD-icon-undo'));
88124                     }
88125                 }
88126
88127
88128                 // Update
88129                 container = container
88130                     .merge(enter);
88131
88132                 container.select('.field-label > .remove-icon')  // propagate bound data
88133                     .on('click', remove);
88134
88135                 container.select('.field-label > .modified-icon')  // propagate bound data
88136                     .on('click', revert);
88137
88138                 container
88139                     .each(function(d) {
88140                         var selection = select(this);
88141
88142                         if (!d.impl) {
88143                             createField();
88144                         }
88145
88146                         var reference, help;
88147
88148                         // instantiate field help
88149                         if (options.wrap && field.type === 'restrictions') {
88150                             help = uiFieldHelp(context, 'restrictions');
88151                         }
88152
88153                         // instantiate tag reference
88154                         if (options.wrap && options.info) {
88155                             var referenceKey = d.key;
88156                             if (d.type === 'multiCombo') {   // lookup key without the trailing ':'
88157                                 referenceKey = referenceKey.replace(/:$/, '');
88158                             }
88159
88160                             reference = uiTagReference(d.reference || { key: referenceKey });
88161                             if (_state === 'hover') {
88162                                 reference.showing(false);
88163                             }
88164                         }
88165
88166                         selection
88167                             .call(d.impl);
88168
88169                         // add field help components
88170                         if (help) {
88171                             selection
88172                                 .call(help.body)
88173                                 .select('.field-label')
88174                                 .call(help.button);
88175                         }
88176
88177                         // add tag reference components
88178                         if (reference) {
88179                             selection
88180                                 .call(reference.body)
88181                                 .select('.field-label')
88182                                 .call(reference.button);
88183                         }
88184
88185                         d.impl.tags(_tags);
88186                     });
88187
88188
88189                     container
88190                         .classed('locked', _locked)
88191                         .classed('modified', isModified())
88192                         .classed('present', tagsContainFieldKey());
88193
88194
88195                     // show a tip and lock icon if the field is locked
88196                     var annotation = container.selectAll('.field-label .label-textannotation');
88197                     var icon = annotation.selectAll('.icon')
88198                         .data(_locked ? [0]: []);
88199
88200                     icon.exit()
88201                         .remove();
88202
88203                     icon.enter()
88204                         .append('svg')
88205                         .attr('class', 'icon')
88206                         .append('use')
88207                         .attr('xlink:href', '#fas-lock');
88208
88209                     container.call(_locked ? _lockedTip : _lockedTip.destroy);
88210             };
88211
88212
88213             field.state = function(val) {
88214                 if (!arguments.length) { return _state; }
88215                 _state = val;
88216                 return field;
88217             };
88218
88219
88220             field.tags = function(val) {
88221                 if (!arguments.length) { return _tags; }
88222                 _tags = val;
88223
88224                 if (tagsContainFieldKey() && !_show) {
88225                     // always show a field if it has a value to display
88226                     _show = true;
88227                     if (!field.impl) {
88228                         createField();
88229                     }
88230                 }
88231
88232                 return field;
88233             };
88234
88235
88236             field.locked = function(val) {
88237                 if (!arguments.length) { return _locked; }
88238                 _locked = val;
88239                 return field;
88240             };
88241
88242
88243             field.show = function() {
88244                 _show = true;
88245                 if (!field.impl) {
88246                     createField();
88247                 }
88248                 if (field.default && field.key && _tags[field.key] !== field.default) {
88249                     var t = {};
88250                     t[field.key] = field.default;
88251                     dispatch$1.call('change', this, t);
88252                 }
88253             };
88254
88255             // A shown field has a visible UI, a non-shown field is in the 'Add field' dropdown
88256             field.isShown = function() {
88257                 return _show;
88258             };
88259
88260
88261             // An allowed field can appear in the UI or in the 'Add field' dropdown.
88262             // A non-allowed field is hidden from the user altogether
88263             field.isAllowed = function() {
88264
88265                 if (entityIDs &&
88266                     entityIDs.length > 1 &&
88267                     uiFields[field.type].supportsMultiselection === false) { return false; }
88268
88269                 if (field.geometry && !entityIDs.every(function(entityID) {
88270                     return field.matchGeometry(context.graph().geometry(entityID));
88271                 })) { return false; }
88272
88273                 if (field.countryCodes || field.notCountryCodes) {
88274                     var extent = combinedEntityExtent();
88275                     if (!extent) { return true; }
88276
88277                     var center = extent.center();
88278                     var countryCode = iso1A2Code(center);
88279
88280                     if (!countryCode) { return false; }
88281
88282                     countryCode = countryCode.toLowerCase();
88283
88284                     if (field.countryCodes && field.countryCodes.indexOf(countryCode) === -1) {
88285                         return false;
88286                     }
88287                     if (field.notCountryCodes && field.notCountryCodes.indexOf(countryCode) !== -1) {
88288                         return false;
88289                     }
88290                 }
88291
88292                 var prerequisiteTag = field.prerequisiteTag;
88293
88294                 if (entityIDs &&
88295                     !tagsContainFieldKey() && // ignore tagging prerequisites if a value is already present
88296                     prerequisiteTag) {
88297
88298                     if (!entityIDs.every(function(entityID) {
88299                         var entity = context.graph().entity(entityID);
88300                         if (prerequisiteTag.key) {
88301                             var value = entity.tags[prerequisiteTag.key];
88302                             if (!value) { return false; }
88303
88304                             if (prerequisiteTag.valueNot) {
88305                                 return prerequisiteTag.valueNot !== value;
88306                             }
88307                             if (prerequisiteTag.value) {
88308                                 return prerequisiteTag.value === value;
88309                             }
88310                         } else if (prerequisiteTag.keyNot) {
88311                             if (entity.tags[prerequisiteTag.keyNot]) { return false; }
88312                         }
88313                         return true;
88314                     })) { return false; }
88315                 }
88316
88317                 return true;
88318             };
88319
88320
88321             field.focus = function() {
88322                 if (field.impl) {
88323                     field.impl.focus();
88324                 }
88325             };
88326
88327
88328             function combinedEntityExtent() {
88329                 return entityIDs && entityIDs.length && entityIDs.reduce(function(extent, entityID) {
88330                     var entity = context.graph().entity(entityID);
88331                     return extent.extend(entity.extent(context.graph()));
88332                 }, geoExtent());
88333             }
88334
88335
88336             return utilRebind(field, dispatch$1, 'on');
88337         }
88338
88339         function uiFormFields(context) {
88340             var moreCombo = uiCombobox(context, 'more-fields').minItems(1);
88341             var _fieldsArr = [];
88342             var _lastPlaceholder = '';
88343             var _state = '';
88344             var _klass = '';
88345
88346
88347             function formFields(selection) {
88348                 var allowedFields = _fieldsArr.filter(function(field) { return field.isAllowed(); });
88349                 var shown = allowedFields.filter(function(field) { return field.isShown(); });
88350                 var notShown = allowedFields.filter(function(field) { return !field.isShown(); });
88351
88352                 var container = selection.selectAll('.form-fields-container')
88353                     .data([0]);
88354
88355                 container = container.enter()
88356                     .append('div')
88357                     .attr('class', 'form-fields-container ' + (_klass || ''))
88358                     .merge(container);
88359
88360
88361                 var fields = container.selectAll('.wrap-form-field')
88362                     .data(shown, function(d) { return d.id + (d.entityIDs ? d.entityIDs.join() : ''); });
88363
88364                 fields.exit()
88365                     .remove();
88366
88367                 // Enter
88368                 var enter = fields.enter()
88369                     .append('div')
88370                     .attr('class', function(d) { return 'wrap-form-field wrap-form-field-' + d.safeid; });
88371
88372                 // Update
88373                 fields = fields
88374                     .merge(enter);
88375
88376                 fields
88377                     .order()
88378                     .each(function(d) {
88379                         select(this)
88380                             .call(d.render);
88381                     });
88382
88383
88384                 var titles = [];
88385                 var moreFields = notShown.map(function(field) {
88386                     var label = field.label();
88387                     titles.push(label);
88388
88389                     var terms = field.terms();
88390                     if (field.key) { terms.push(field.key); }
88391                     if (field.keys) { terms = terms.concat(field.keys); }
88392
88393                     return {
88394                         title: label,
88395                         value: label,
88396                         field: field,
88397                         terms: terms
88398                     };
88399                 });
88400
88401                 var placeholder = titles.slice(0,3).join(', ') + ((titles.length > 3) ? '…' : '');
88402
88403
88404                 var more = selection.selectAll('.more-fields')
88405                     .data((_state === 'hover' || moreFields.length === 0) ? [] : [0]);
88406
88407                 more.exit()
88408                     .remove();
88409
88410                 var moreEnter = more.enter()
88411                     .append('div')
88412                     .attr('class', 'more-fields')
88413                     .append('label');
88414
88415                 moreEnter
88416                     .append('span')
88417                     .text(_t('inspector.add_fields'));
88418
88419                 more = moreEnter
88420                     .merge(more);
88421
88422
88423                 var input = more.selectAll('.value')
88424                     .data([0]);
88425
88426                 input.exit()
88427                     .remove();
88428
88429                 input = input.enter()
88430                     .append('input')
88431                     .attr('class', 'value')
88432                     .attr('type', 'text')
88433                     .attr('placeholder', placeholder)
88434                     .call(utilNoAuto)
88435                     .merge(input);
88436
88437                 input
88438                     .call(utilGetSetValue, '')
88439                     .call(moreCombo
88440                         .data(moreFields)
88441                         .on('accept', function (d) {
88442                             if (!d) { return; }  // user entered something that was not matched
88443                             var field = d.field;
88444                             field.show();
88445                             selection.call(formFields);  // rerender
88446                             if (field.type !== 'semiCombo' && field.type !== 'multiCombo') {
88447                                 field.focus();
88448                             }
88449                         })
88450                     );
88451
88452                 // avoid updating placeholder excessively (triggers style recalc)
88453                 if (_lastPlaceholder !== placeholder) {
88454                     input.attr('placeholder', placeholder);
88455                     _lastPlaceholder = placeholder;
88456                 }
88457             }
88458
88459
88460             formFields.fieldsArr = function(val) {
88461                 if (!arguments.length) { return _fieldsArr; }
88462                 _fieldsArr = val || [];
88463                 return formFields;
88464             };
88465
88466             formFields.state = function(val) {
88467                 if (!arguments.length) { return _state; }
88468                 _state = val;
88469                 return formFields;
88470             };
88471
88472             formFields.klass = function(val) {
88473                 if (!arguments.length) { return _klass; }
88474                 _klass = val;
88475                 return formFields;
88476             };
88477
88478
88479             return formFields;
88480         }
88481
88482         function uiSectionPresetFields(context) {
88483
88484             var section = uiSection('preset-fields', context)
88485                 .title(function() {
88486                     return _t('inspector.fields');
88487                 })
88488                 .disclosureContent(renderDisclosureContent);
88489
88490             var dispatch$1 = dispatch('change', 'revert');
88491             var formFields = uiFormFields(context);
88492             var _state;
88493             var _fieldsArr;
88494             var _presets = [];
88495             var _tags;
88496             var _entityIDs;
88497
88498             function renderDisclosureContent(selection) {
88499                 if (!_fieldsArr) {
88500
88501                     var graph = context.graph();
88502
88503                     var geometries = Object.keys(_entityIDs.reduce(function(geoms, entityID) {
88504                         return geoms[graph.entity(entityID).geometry(graph)] = true;
88505                     }, {}));
88506
88507                     var presetsManager = _mainPresetIndex;
88508
88509                     var allFields = [];
88510                     var allMoreFields = [];
88511                     var sharedTotalFields;
88512
88513                     _presets.forEach(function(preset) {
88514                         var fields = preset.fields();
88515                         var moreFields = preset.moreFields();
88516
88517                         allFields = utilArrayUnion(allFields, fields);
88518                         allMoreFields = utilArrayUnion(allMoreFields, moreFields);
88519
88520                         if (!sharedTotalFields) {
88521                             sharedTotalFields = utilArrayUnion(fields, moreFields);
88522                         } else {
88523                             sharedTotalFields = sharedTotalFields.filter(function(field) {
88524                                 return fields.indexOf(field) !== -1 || moreFields.indexOf(field) !== -1;
88525                             });
88526                         }
88527                     });
88528
88529                     var sharedFields = allFields.filter(function(field) {
88530                         return sharedTotalFields.indexOf(field) !== -1;
88531                     });
88532                     var sharedMoreFields = allMoreFields.filter(function(field) {
88533                         return sharedTotalFields.indexOf(field) !== -1;
88534                     });
88535
88536                     _fieldsArr = [];
88537
88538                     sharedFields.forEach(function(field) {
88539                         if (field.matchAllGeometry(geometries)) {
88540                             _fieldsArr.push(
88541                                 uiField(context, field, _entityIDs)
88542                             );
88543                         }
88544                     });
88545
88546                     var singularEntity = _entityIDs.length === 1 && graph.hasEntity(_entityIDs[0]);
88547                     if (singularEntity && singularEntity.isHighwayIntersection(graph) && presetsManager.field('restrictions')) {
88548                         _fieldsArr.push(
88549                             uiField(context, presetsManager.field('restrictions'), _entityIDs)
88550                         );
88551                     }
88552
88553                     var additionalFields = utilArrayUnion(sharedMoreFields, presetsManager.universal());
88554                     additionalFields.sort(function(field1, field2) {
88555                         return field1.label().localeCompare(field2.label(), _mainLocalizer.localeCode());
88556                     });
88557
88558                     additionalFields.forEach(function(field) {
88559                         if (sharedFields.indexOf(field) === -1 &&
88560                             field.matchAllGeometry(geometries)) {
88561                             _fieldsArr.push(
88562                                 uiField(context, field, _entityIDs, { show: false })
88563                             );
88564                         }
88565                     });
88566
88567                     _fieldsArr.forEach(function(field) {
88568                         field
88569                             .on('change', function(t, onInput) {
88570                                 dispatch$1.call('change', field, _entityIDs, t, onInput);
88571                             })
88572                             .on('revert', function(keys) {
88573                                 dispatch$1.call('revert', field, keys);
88574                             });
88575                     });
88576                 }
88577
88578                 _fieldsArr.forEach(function(field) {
88579                     field
88580                         .state(_state)
88581                         .tags(_tags);
88582                 });
88583
88584
88585                 selection
88586                     .call(formFields
88587                         .fieldsArr(_fieldsArr)
88588                         .state(_state)
88589                         .klass('grouped-items-area')
88590                     );
88591
88592
88593                 selection.selectAll('.wrap-form-field input')
88594                     .on('keydown', function() {
88595                         // if user presses enter, and combobox is not active, accept edits..
88596                         if (event.keyCode === 13 && context.container().select('.combobox').empty()) {
88597                             context.enter(modeBrowse(context));
88598                         }
88599                     });
88600             }
88601
88602             section.presets = function(val) {
88603                 if (!arguments.length) { return _presets; }
88604                 if (!_presets || !val || !utilArrayIdentical(_presets, val)) {
88605                     _presets = val;
88606                     _fieldsArr = null;
88607                 }
88608                 return section;
88609             };
88610
88611             section.state = function(val) {
88612                 if (!arguments.length) { return _state; }
88613                 _state = val;
88614                 return section;
88615             };
88616
88617             section.tags = function(val) {
88618                 if (!arguments.length) { return _tags; }
88619                 _tags = val;
88620                 // Don't reset _fieldsArr here.
88621                 return section;
88622             };
88623
88624             section.entityIDs = function(val) {
88625                 if (!arguments.length) { return _entityIDs; }
88626                 if (!val || !_entityIDs || !utilArrayIdentical(_entityIDs, val)) {
88627                     _entityIDs = val;
88628                     _fieldsArr = null;
88629                 }
88630                 return section;
88631             };
88632
88633             return utilRebind(section, dispatch$1, 'on');
88634         }
88635
88636         function uiSectionRawMemberEditor(context) {
88637
88638             var section = uiSection('raw-member-editor', context)
88639                 .shouldDisplay(function() {
88640                     if (!_entityIDs || _entityIDs.length !== 1) { return false; }
88641
88642                     var entity = context.hasEntity(_entityIDs[0]);
88643                     return entity && entity.type === 'relation';
88644                 })
88645                 .title(function() {
88646                     var entity = context.hasEntity(_entityIDs[0]);
88647                     if (!entity) { return ''; }
88648
88649                     var gt = entity.members.length > _maxMembers ? '>' : '';
88650                     var count = gt + entity.members.slice(0, _maxMembers).length;
88651                     return _t('inspector.title_count', { title: _t('inspector.members'), count: count });
88652                 })
88653                 .disclosureContent(renderDisclosureContent);
88654
88655             var taginfo = services.taginfo;
88656             var _entityIDs;
88657             var _maxMembers = 1000;
88658
88659             function downloadMember(d) {
88660                 event.preventDefault();
88661
88662                 // display the loading indicator
88663                 select(this.parentNode).classed('tag-reference-loading', true);
88664                 context.loadEntity(d.id, function() {
88665                     section.reRender();
88666                 });
88667             }
88668
88669             function zoomToMember(d) {
88670                 event.preventDefault();
88671
88672                 var entity = context.entity(d.id);
88673                 context.map().zoomToEase(entity);
88674
88675                 // highlight the feature in case it wasn't previously on-screen
88676                 utilHighlightEntities([d.id], true, context);
88677             }
88678
88679
88680             function selectMember(d) {
88681                 event.preventDefault();
88682
88683                 // remove the hover-highlight styling
88684                 utilHighlightEntities([d.id], false, context);
88685
88686                 var entity = context.entity(d.id);
88687                 var mapExtent = context.map().extent();
88688                 if (!entity.intersects(mapExtent, context.graph())) {
88689                     // zoom to the entity if its extent is not visible now
88690                     context.map().zoomToEase(entity);
88691                 }
88692
88693                 context.enter(modeSelect(context, [d.id]));
88694             }
88695
88696
88697             function changeRole(d) {
88698                 var oldRole = d.role;
88699                 var newRole = context.cleanRelationRole(select(this).property('value'));
88700
88701                 if (oldRole !== newRole) {
88702                     var member = { id: d.id, type: d.type, role: newRole };
88703                     context.perform(
88704                         actionChangeMember(d.relation.id, member, d.index),
88705                         _t('operations.change_role.annotation')
88706                     );
88707                 }
88708             }
88709
88710
88711             function deleteMember(d) {
88712
88713                 // remove the hover-highlight styling
88714                 utilHighlightEntities([d.id], false, context);
88715
88716                 context.perform(
88717                     actionDeleteMember(d.relation.id, d.index),
88718                     _t('operations.delete_member.annotation')
88719                 );
88720
88721                 if (!context.hasEntity(d.relation.id)) {
88722                     context.enter(modeBrowse(context));
88723                 }
88724             }
88725
88726             function renderDisclosureContent(selection) {
88727
88728                 var entityID = _entityIDs[0];
88729
88730                 var memberships = [];
88731                 var entity = context.entity(entityID);
88732                 entity.members.slice(0, _maxMembers).forEach(function(member, index) {
88733                     memberships.push({
88734                         index: index,
88735                         id: member.id,
88736                         type: member.type,
88737                         role: member.role,
88738                         relation: entity,
88739                         member: context.hasEntity(member.id),
88740                         domId: utilUniqueDomId(entityID + '-member-' + index)
88741                     });
88742                 });
88743
88744                 var list = selection.selectAll('.member-list')
88745                     .data([0]);
88746
88747                 list = list.enter()
88748                     .append('ul')
88749                     .attr('class', 'member-list')
88750                     .merge(list);
88751
88752
88753                 var items = list.selectAll('li')
88754                     .data(memberships, function(d) {
88755                         return osmEntity.key(d.relation) + ',' + d.index + ',' +
88756                             (d.member ? osmEntity.key(d.member) : 'incomplete');
88757                     });
88758
88759                 items.exit()
88760                     .each(unbind)
88761                     .remove();
88762
88763                 var itemsEnter = items.enter()
88764                     .append('li')
88765                     .attr('class', 'member-row form-field')
88766                     .classed('member-incomplete', function(d) { return !d.member; });
88767
88768                 itemsEnter
88769                     .each(function(d) {
88770                         var item = select(this);
88771
88772                         var label = item
88773                             .append('label')
88774                             .attr('class', 'field-label')
88775                             .attr('for', d.domId);
88776
88777                         if (d.member) {
88778                             // highlight the member feature in the map while hovering on the list item
88779                             item
88780                                 .on('mouseover', function() {
88781                                     utilHighlightEntities([d.id], true, context);
88782                                 })
88783                                 .on('mouseout', function() {
88784                                     utilHighlightEntities([d.id], false, context);
88785                                 });
88786
88787                             var labelLink = label
88788                                 .append('span')
88789                                 .attr('class', 'label-text')
88790                                 .append('a')
88791                                 .attr('href', '#')
88792                                 .on('click', selectMember);
88793
88794                             labelLink
88795                                 .append('span')
88796                                 .attr('class', 'member-entity-type')
88797                                 .text(function(d) {
88798                                     var matched = _mainPresetIndex.match(d.member, context.graph());
88799                                     return (matched && matched.name()) || utilDisplayType(d.member.id);
88800                                 });
88801
88802                             labelLink
88803                                 .append('span')
88804                                 .attr('class', 'member-entity-name')
88805                                 .text(function(d) { return utilDisplayName(d.member); });
88806
88807                             label
88808                                 .append('button')
88809                                 .attr('tabindex', -1)
88810                                 .attr('title', _t('icons.remove'))
88811                                 .attr('class', 'remove member-delete')
88812                                 .call(svgIcon('#iD-operation-delete'));
88813
88814                             label
88815                                 .append('button')
88816                                 .attr('class', 'member-zoom')
88817                                 .attr('title', _t('icons.zoom_to'))
88818                                 .call(svgIcon('#iD-icon-framed-dot', 'monochrome'))
88819                                 .on('click', zoomToMember);
88820
88821                         } else {
88822                             var labelText = label
88823                                 .append('span')
88824                                 .attr('class', 'label-text');
88825
88826                             labelText
88827                                 .append('span')
88828                                 .attr('class', 'member-entity-type')
88829                                 .text(_t('inspector.' + d.type, { id: d.id }));
88830
88831                             labelText
88832                                 .append('span')
88833                                 .attr('class', 'member-entity-name')
88834                                 .text(_t('inspector.incomplete', { id: d.id }));
88835
88836                             label
88837                                 .append('button')
88838                                 .attr('class', 'member-download')
88839                                 .attr('title', _t('icons.download'))
88840                                 .attr('tabindex', -1)
88841                                 .call(svgIcon('#iD-icon-load'))
88842                                 .on('click', downloadMember);
88843                         }
88844                     });
88845
88846                 var wrapEnter = itemsEnter
88847                     .append('div')
88848                     .attr('class', 'form-field-input-wrap form-field-input-member');
88849
88850                 wrapEnter
88851                     .append('input')
88852                     .attr('class', 'member-role')
88853                     .attr('id', function(d) {
88854                         return d.domId;
88855                     })
88856                     .property('type', 'text')
88857                     .attr('placeholder', _t('inspector.role'))
88858                     .call(utilNoAuto);
88859
88860                 if (taginfo) {
88861                     wrapEnter.each(bindTypeahead);
88862                 }
88863
88864                 // update
88865                 items = items
88866                     .merge(itemsEnter)
88867                     .order();
88868
88869                 items.select('input.member-role')
88870                     .property('value', function(d) { return d.role; })
88871                     .on('blur', changeRole)
88872                     .on('change', changeRole);
88873
88874                 items.select('button.member-delete')
88875                     .on('click', deleteMember);
88876
88877                 var dragOrigin, targetIndex;
88878
88879                 items.call(d3_drag()
88880                     .on('start', function() {
88881                         dragOrigin = {
88882                             x: event.x,
88883                             y: event.y
88884                         };
88885                         targetIndex = null;
88886                     })
88887                     .on('drag', function(d, index) {
88888                         var x = event.x - dragOrigin.x,
88889                             y = event.y - dragOrigin.y;
88890
88891                         if (!select(this).classed('dragging') &&
88892                             // don't display drag until dragging beyond a distance threshold
88893                             Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)) <= 5) { return; }
88894
88895                         select(this)
88896                             .classed('dragging', true);
88897
88898                         targetIndex = null;
88899
88900                         selection.selectAll('li.member-row')
88901                             .style('transform', function(d2, index2) {
88902                                 var node = select(this).node();
88903                                 if (index === index2) {
88904                                     return 'translate(' + x + 'px, ' + y + 'px)';
88905                                 } else if (index2 > index && event.y > node.offsetTop) {
88906                                     if (targetIndex === null || index2 > targetIndex) {
88907                                         targetIndex = index2;
88908                                     }
88909                                     return 'translateY(-100%)';
88910                                 } else if (index2 < index && event.y < node.offsetTop + node.offsetHeight) {
88911                                     if (targetIndex === null || index2 < targetIndex) {
88912                                         targetIndex = index2;
88913                                     }
88914                                     return 'translateY(100%)';
88915                                 }
88916                                 return null;
88917                             });
88918                     })
88919                     .on('end', function(d, index) {
88920
88921                         if (!select(this).classed('dragging')) {
88922                             return;
88923                         }
88924
88925                         select(this)
88926                             .classed('dragging', false);
88927
88928                         selection.selectAll('li.member-row')
88929                             .style('transform', null);
88930
88931                         if (targetIndex !== null) {
88932                             // dragged to a new position, reorder
88933                             context.perform(
88934                                 actionMoveMember(d.relation.id, index, targetIndex),
88935                                 _t('operations.reorder_members.annotation')
88936                             );
88937                         }
88938                     })
88939                 );
88940
88941
88942
88943                 function bindTypeahead(d) {
88944                     var row = select(this);
88945                     var role = row.selectAll('input.member-role');
88946                     var origValue = role.property('value');
88947
88948                     function sort(value, data) {
88949                         var sameletter = [];
88950                         var other = [];
88951                         for (var i = 0; i < data.length; i++) {
88952                             if (data[i].value.substring(0, value.length) === value) {
88953                                 sameletter.push(data[i]);
88954                             } else {
88955                                 other.push(data[i]);
88956                             }
88957                         }
88958                         return sameletter.concat(other);
88959                     }
88960
88961                     role.call(uiCombobox(context, 'member-role')
88962                         .fetcher(function(role, callback) {
88963                             // The `geometry` param is used in the `taginfo.js` interface for
88964                             // filtering results, as a key into the `tag_members_fractions`
88965                             // object.  If we don't know the geometry because the member is
88966                             // not yet downloaded, it's ok to guess based on type.
88967                             var geometry;
88968                             if (d.member) {
88969                                 geometry = context.graph().geometry(d.member.id);
88970                             } else if (d.type === 'relation') {
88971                                 geometry = 'relation';
88972                             } else if (d.type === 'way') {
88973                                 geometry = 'line';
88974                             } else {
88975                                 geometry = 'point';
88976                             }
88977
88978                             var rtype = entity.tags.type;
88979                             taginfo.roles({
88980                                 debounce: true,
88981                                 rtype: rtype || '',
88982                                 geometry: geometry,
88983                                 query: role
88984                             }, function(err, data) {
88985                                 if (!err) { callback(sort(role, data)); }
88986                             });
88987                         })
88988                         .on('cancel', function() {
88989                             role.property('value', origValue);
88990                         })
88991                     );
88992                 }
88993
88994
88995                 function unbind() {
88996                     var row = select(this);
88997
88998                     row.selectAll('input.member-role')
88999                         .call(uiCombobox.off, context);
89000                 }
89001             }
89002
89003             section.entityIDs = function(val) {
89004                 if (!arguments.length) { return _entityIDs; }
89005                 _entityIDs = val;
89006                 return section;
89007             };
89008
89009
89010             return section;
89011         }
89012
89013         function uiSectionRawMembershipEditor(context) {
89014
89015             var section = uiSection('raw-membership-editor', context)
89016                 .shouldDisplay(function() {
89017                     return _entityIDs && _entityIDs.length === 1;
89018                 })
89019                 .title(function() {
89020                     var entity = context.hasEntity(_entityIDs[0]);
89021                     if (!entity) { return ''; }
89022
89023                     var parents = context.graph().parentRelations(entity);
89024                     var gt = parents.length > _maxMemberships ? '>' : '';
89025                     var count = gt + parents.slice(0, _maxMemberships).length;
89026                     return _t('inspector.title_count', { title: _t('inspector.relations'), count: count });
89027                 })
89028                 .disclosureContent(renderDisclosureContent);
89029
89030             var taginfo = services.taginfo;
89031             var nearbyCombo = uiCombobox(context, 'parent-relation')
89032                 .minItems(1)
89033                 .fetcher(fetchNearbyRelations)
89034                 .itemsMouseEnter(function(d) {
89035                     if (d.relation) { utilHighlightEntities([d.relation.id], true, context); }
89036                 })
89037                 .itemsMouseLeave(function(d) {
89038                     if (d.relation) { utilHighlightEntities([d.relation.id], false, context); }
89039                 });
89040             var _inChange = false;
89041             var _entityIDs = [];
89042             var _showBlank;
89043             var _maxMemberships = 1000;
89044
89045             function selectRelation(d) {
89046                 event.preventDefault();
89047
89048                 // remove the hover-highlight styling
89049                 utilHighlightEntities([d.relation.id], false, context);
89050
89051                 context.enter(modeSelect(context, [d.relation.id]));
89052             }
89053
89054             function zoomToRelation(d) {
89055                 event.preventDefault();
89056
89057                 var entity = context.entity(d.relation.id);
89058                 context.map().zoomToEase(entity);
89059
89060                 // highlight the relation in case it wasn't previously on-screen
89061                 utilHighlightEntities([d.relation.id], true, context);
89062             }
89063
89064
89065             function changeRole(d) {
89066                 if (d === 0) { return; }    // called on newrow (shoudn't happen)
89067                 if (_inChange) { return; }  // avoid accidental recursive call #5731
89068
89069                 var oldRole = d.member.role;
89070                 var newRole = context.cleanRelationRole(select(this).property('value'));
89071
89072                 if (oldRole !== newRole) {
89073                     _inChange = true;
89074                     context.perform(
89075                         actionChangeMember(d.relation.id, Object.assign({}, d.member, { role: newRole }), d.index),
89076                         _t('operations.change_role.annotation')
89077                     );
89078                 }
89079                 _inChange = false;
89080             }
89081
89082
89083             function addMembership(d, role) {
89084                 this.blur();           // avoid keeping focus on the button
89085                 _showBlank = false;
89086
89087                 var member = { id: _entityIDs[0], type: context.entity(_entityIDs[0]).type, role: role };
89088
89089                 if (d.relation) {
89090                     context.perform(
89091                         actionAddMember(d.relation.id, member),
89092                         _t('operations.add_member.annotation')
89093                     );
89094
89095                 } else {
89096                     var relation = osmRelation();
89097                     context.perform(
89098                         actionAddEntity(relation),
89099                         actionAddMember(relation.id, member),
89100                         _t('operations.add.annotation.relation')
89101                     );
89102
89103                     context.enter(modeSelect(context, [relation.id]).newFeature(true));
89104                 }
89105             }
89106
89107
89108             function deleteMembership(d) {
89109                 this.blur();           // avoid keeping focus on the button
89110                 if (d === 0) { return; }   // called on newrow (shoudn't happen)
89111
89112                 // remove the hover-highlight styling
89113                 utilHighlightEntities([d.relation.id], false, context);
89114
89115                 context.perform(
89116                     actionDeleteMember(d.relation.id, d.index),
89117                     _t('operations.delete_member.annotation')
89118                 );
89119             }
89120
89121
89122             function fetchNearbyRelations(q, callback) {
89123                 var newRelation = { relation: null, value: _t('inspector.new_relation') };
89124
89125                 var entityID = _entityIDs[0];
89126
89127                 var result = [];
89128
89129                 var graph = context.graph();
89130
89131                 function baseDisplayLabel(entity) {
89132                     var matched = _mainPresetIndex.match(entity, graph);
89133                     var presetName = (matched && matched.name()) || _t('inspector.relation');
89134                     var entityName = utilDisplayName(entity) || '';
89135
89136                     return presetName + ' ' + entityName;
89137                 }
89138
89139                 var explicitRelation = q && context.hasEntity(q.toLowerCase());
89140                 if (explicitRelation && explicitRelation.type === 'relation' && explicitRelation.id !== entityID) {
89141                     // loaded relation is specified explicitly, only show that
89142
89143                     result.push({
89144                         relation: explicitRelation,
89145                         value: baseDisplayLabel(explicitRelation) + ' ' + explicitRelation.id
89146                     });
89147                 } else {
89148
89149                     context.history().intersects(context.map().extent()).forEach(function(entity) {
89150                         if (entity.type !== 'relation' || entity.id === entityID) { return; }
89151
89152                         var value = baseDisplayLabel(entity);
89153                         if (q && (value + ' ' + entity.id).toLowerCase().indexOf(q.toLowerCase()) === -1) { return; }
89154
89155                         result.push({ relation: entity, value: value });
89156                     });
89157
89158                     result.sort(function(a, b) {
89159                         return osmRelation.creationOrder(a.relation, b.relation);
89160                     });
89161
89162                     // Dedupe identical names by appending relation id - see #2891
89163                     var dupeGroups = Object.values(utilArrayGroupBy(result, 'value'))
89164                         .filter(function(v) { return v.length > 1; });
89165
89166                     dupeGroups.forEach(function(group) {
89167                         group.forEach(function(obj) {
89168                             obj.value += ' ' + obj.relation.id;
89169                         });
89170                     });
89171                 }
89172
89173                 result.forEach(function(obj) {
89174                     obj.title = obj.value;
89175                 });
89176
89177                 result.unshift(newRelation);
89178                 callback(result);
89179             }
89180
89181             function renderDisclosureContent(selection) {
89182
89183                 var entityID = _entityIDs[0];
89184
89185                 var entity = context.entity(entityID);
89186                 var parents = context.graph().parentRelations(entity);
89187
89188                 var memberships = [];
89189
89190                 parents.slice(0, _maxMemberships).forEach(function(relation) {
89191                     relation.members.forEach(function(member, index) {
89192                         if (member.id === entity.id) {
89193                             memberships.push({
89194                                 relation: relation,
89195                                 member: member,
89196                                 index: index,
89197                                 domId: utilUniqueDomId(entityID + '-membership-' + relation.id + '-' + index)
89198                             });
89199                         }
89200                     });
89201                 });
89202
89203                 var list = selection.selectAll('.member-list')
89204                     .data([0]);
89205
89206                 list = list.enter()
89207                     .append('ul')
89208                     .attr('class', 'member-list')
89209                     .merge(list);
89210
89211
89212                 var items = list.selectAll('li.member-row-normal')
89213                     .data(memberships, function(d) {
89214                         return osmEntity.key(d.relation) + ',' + d.index;
89215                     });
89216
89217                 items.exit()
89218                     .each(unbind)
89219                     .remove();
89220
89221                 // Enter
89222                 var itemsEnter = items.enter()
89223                     .append('li')
89224                     .attr('class', 'member-row member-row-normal form-field');
89225
89226                 // highlight the relation in the map while hovering on the list item
89227                 itemsEnter.on('mouseover', function(d) {
89228                         utilHighlightEntities([d.relation.id], true, context);
89229                     })
89230                     .on('mouseout', function(d) {
89231                         utilHighlightEntities([d.relation.id], false, context);
89232                     });
89233
89234                 var labelEnter = itemsEnter
89235                     .append('label')
89236                     .attr('class', 'field-label')
89237                     .attr('for', function(d) {
89238                         return d.domId;
89239                     });
89240
89241                 var labelLink = labelEnter
89242                     .append('span')
89243                     .attr('class', 'label-text')
89244                     .append('a')
89245                     .attr('href', '#')
89246                     .on('click', selectRelation);
89247
89248                 labelLink
89249                     .append('span')
89250                     .attr('class', 'member-entity-type')
89251                     .text(function(d) {
89252                         var matched = _mainPresetIndex.match(d.relation, context.graph());
89253                         return (matched && matched.name()) || _t('inspector.relation');
89254                     });
89255
89256                 labelLink
89257                     .append('span')
89258                     .attr('class', 'member-entity-name')
89259                     .text(function(d) { return utilDisplayName(d.relation); });
89260
89261                 labelEnter
89262                     .append('button')
89263                     .attr('tabindex', -1)
89264                     .attr('class', 'remove member-delete')
89265                     .call(svgIcon('#iD-operation-delete'))
89266                     .on('click', deleteMembership);
89267
89268                 labelEnter
89269                     .append('button')
89270                     .attr('class', 'member-zoom')
89271                     .attr('title', _t('icons.zoom_to'))
89272                     .call(svgIcon('#iD-icon-framed-dot', 'monochrome'))
89273                     .on('click', zoomToRelation);
89274
89275                 var wrapEnter = itemsEnter
89276                     .append('div')
89277                     .attr('class', 'form-field-input-wrap form-field-input-member');
89278
89279                 wrapEnter
89280                     .append('input')
89281                     .attr('class', 'member-role')
89282                     .attr('id', function(d) {
89283                         return d.domId;
89284                     })
89285                     .property('type', 'text')
89286                     .attr('placeholder', _t('inspector.role'))
89287                     .call(utilNoAuto)
89288                     .property('value', function(d) { return d.member.role; })
89289                     .on('blur', changeRole)
89290                     .on('change', changeRole);
89291
89292                 if (taginfo) {
89293                     wrapEnter.each(bindTypeahead);
89294                 }
89295
89296
89297                 var newMembership = list.selectAll('.member-row-new')
89298                     .data(_showBlank ? [0] : []);
89299
89300                 // Exit
89301                 newMembership.exit()
89302                     .remove();
89303
89304                 // Enter
89305                 var newMembershipEnter = newMembership.enter()
89306                     .append('li')
89307                     .attr('class', 'member-row member-row-new form-field');
89308
89309                 var newLabelEnter = newMembershipEnter
89310                     .append('label')
89311                     .attr('class', 'field-label');
89312
89313                 newLabelEnter
89314                     .append('input')
89315                     .attr('placeholder', _t('inspector.choose_relation'))
89316                     .attr('type', 'text')
89317                     .attr('class', 'member-entity-input')
89318                     .call(utilNoAuto);
89319
89320                 newLabelEnter
89321                     .append('button')
89322                     .attr('tabindex', -1)
89323                     .attr('class', 'remove member-delete')
89324                     .call(svgIcon('#iD-operation-delete'))
89325                     .on('click', function() {
89326                         list.selectAll('.member-row-new')
89327                             .remove();
89328                     });
89329
89330                 var newWrapEnter = newMembershipEnter
89331                     .append('div')
89332                     .attr('class', 'form-field-input-wrap form-field-input-member');
89333
89334                 newWrapEnter
89335                     .append('input')
89336                     .attr('class', 'member-role')
89337                     .property('type', 'text')
89338                     .attr('placeholder', _t('inspector.role'))
89339                     .call(utilNoAuto);
89340
89341                 // Update
89342                 newMembership = newMembership
89343                     .merge(newMembershipEnter);
89344
89345                 newMembership.selectAll('.member-entity-input')
89346                     .on('blur', cancelEntity)   // if it wasn't accepted normally, cancel it
89347                     .call(nearbyCombo
89348                         .on('accept', acceptEntity)
89349                         .on('cancel', cancelEntity)
89350                     );
89351
89352
89353                 // Container for the Add button
89354                 var addRow = selection.selectAll('.add-row')
89355                     .data([0]);
89356
89357                 // enter
89358                 var addRowEnter = addRow.enter()
89359                     .append('div')
89360                     .attr('class', 'add-row');
89361
89362                 var addRelationButton = addRowEnter
89363                     .append('button')
89364                     .attr('class', 'add-relation');
89365
89366                 addRelationButton
89367                     .call(svgIcon('#iD-icon-plus', 'light'));
89368                 addRelationButton
89369                     .call(uiTooltip().title(_t('inspector.add_to_relation')).placement(_mainLocalizer.textDirection() === 'ltr' ? 'right' : 'left'));
89370
89371                 addRowEnter
89372                     .append('div')
89373                     .attr('class', 'space-value');   // preserve space
89374
89375                 addRowEnter
89376                     .append('div')
89377                     .attr('class', 'space-buttons');  // preserve space
89378
89379                 // update
89380                 addRow = addRow
89381                     .merge(addRowEnter);
89382
89383                 addRow.select('.add-relation')
89384                     .on('click', function() {
89385                         _showBlank = true;
89386                         section.reRender();
89387                         list.selectAll('.member-entity-input').node().focus();
89388                     });
89389
89390
89391                 function acceptEntity(d) {
89392                     if (!d) {
89393                         cancelEntity();
89394                         return;
89395                     }
89396                     // remove hover-higlighting
89397                     if (d.relation) { utilHighlightEntities([d.relation.id], false, context); }
89398
89399                     var role = context.cleanRelationRole(list.selectAll('.member-row-new .member-role').property('value'));
89400                     addMembership(d, role);
89401                 }
89402
89403
89404                 function cancelEntity() {
89405                     var input = newMembership.selectAll('.member-entity-input');
89406                     input.property('value', '');
89407
89408                     // remove hover-higlighting
89409                     context.surface().selectAll('.highlighted')
89410                         .classed('highlighted', false);
89411                 }
89412
89413
89414                 function bindTypeahead(d) {
89415                     var row = select(this);
89416                     var role = row.selectAll('input.member-role');
89417                     var origValue = role.property('value');
89418
89419                     function sort(value, data) {
89420                         var sameletter = [];
89421                         var other = [];
89422                         for (var i = 0; i < data.length; i++) {
89423                             if (data[i].value.substring(0, value.length) === value) {
89424                                 sameletter.push(data[i]);
89425                             } else {
89426                                 other.push(data[i]);
89427                             }
89428                         }
89429                         return sameletter.concat(other);
89430                     }
89431
89432                     role.call(uiCombobox(context, 'member-role')
89433                         .fetcher(function(role, callback) {
89434                             var rtype = d.relation.tags.type;
89435                             taginfo.roles({
89436                                 debounce: true,
89437                                 rtype: rtype || '',
89438                                 geometry: context.graph().geometry(entityID),
89439                                 query: role
89440                             }, function(err, data) {
89441                                 if (!err) { callback(sort(role, data)); }
89442                             });
89443                         })
89444                         .on('cancel', function() {
89445                             role.property('value', origValue);
89446                         })
89447                     );
89448                 }
89449
89450
89451                 function unbind() {
89452                     var row = select(this);
89453
89454                     row.selectAll('input.member-role')
89455                         .call(uiCombobox.off, context);
89456                 }
89457             }
89458
89459
89460             section.entityIDs = function(val) {
89461                 if (!arguments.length) { return _entityIDs; }
89462                 _entityIDs = val;
89463                 _showBlank = false;
89464                 return section;
89465             };
89466
89467
89468             return section;
89469         }
89470
89471         function uiSectionSelectionList(context) {
89472
89473             var _selectedIDs = [];
89474
89475             var section = uiSection('selected-features', context)
89476                 .shouldDisplay(function() {
89477                     return _selectedIDs.length > 1;
89478                 })
89479                 .title(function() {
89480                     return _t('inspector.title_count', { title: _t('inspector.features'), count: _selectedIDs.length });
89481                 })
89482                 .disclosureContent(renderDisclosureContent);
89483
89484             context.history()
89485                 .on('change.selectionList', function(difference) {
89486                     if (difference) {
89487                         section.reRender();
89488                     }
89489                 });
89490
89491             section.entityIDs = function(val) {
89492                 if (!arguments.length) { return _selectedIDs; }
89493                 _selectedIDs = val;
89494                 return section;
89495             };
89496
89497             function selectEntity(entity) {
89498                 context.enter(modeSelect(context, [entity.id]));
89499             }
89500
89501             function deselectEntity(entity) {
89502                 event.stopPropagation();
89503
89504                 var selectedIDs = _selectedIDs.slice();
89505                 var index = selectedIDs.indexOf(entity.id);
89506                 if (index > -1) {
89507                     selectedIDs.splice(index, 1);
89508                     context.enter(modeSelect(context, selectedIDs));
89509                 }
89510             }
89511
89512             function renderDisclosureContent(selection) {
89513
89514                 var list = selection.selectAll('.feature-list')
89515                     .data([0]);
89516
89517                 list = list.enter()
89518                     .append('div')
89519                     .attr('class', 'feature-list')
89520                     .merge(list);
89521
89522                 var entities = _selectedIDs
89523                     .map(function(id) { return context.hasEntity(id); })
89524                     .filter(Boolean);
89525
89526                 var items = list.selectAll('.feature-list-item')
89527                     .data(entities, osmEntity.key);
89528
89529                 items.exit()
89530                     .remove();
89531
89532                 // Enter
89533                 var enter = items.enter()
89534                     .append('div')
89535                     .attr('class', 'feature-list-item')
89536                     .on('click', selectEntity);
89537
89538                 enter
89539                     .each(function(d) {
89540                         select(this).on('mouseover', function() {
89541                             utilHighlightEntities([d.id], true, context);
89542                         });
89543                         select(this).on('mouseout', function() {
89544                             utilHighlightEntities([d.id], false, context);
89545                         });
89546                     });
89547
89548                 var label = enter
89549                     .append('button')
89550                     .attr('class', 'label');
89551
89552                 enter
89553                     .append('button')
89554                     .attr('class', 'close')
89555                     .attr('title', _t('icons.deselect'))
89556                     .on('click', deselectEntity)
89557                     .call(svgIcon('#iD-icon-close'));
89558
89559                 label
89560                     .append('span')
89561                     .attr('class', 'entity-geom-icon')
89562                     .call(svgIcon('', 'pre-text'));
89563
89564                 label
89565                     .append('span')
89566                     .attr('class', 'entity-type');
89567
89568                 label
89569                     .append('span')
89570                     .attr('class', 'entity-name');
89571
89572                 // Update
89573                 items = items.merge(enter);
89574
89575                 items.selectAll('.entity-geom-icon use')
89576                     .attr('href', function() {
89577                         var entity = this.parentNode.parentNode.__data__;
89578                         return '#iD-icon-' + entity.geometry(context.graph());
89579                     });
89580
89581                 items.selectAll('.entity-type')
89582                     .text(function(entity) { return _mainPresetIndex.match(entity, context.graph()).name(); });
89583
89584                 items.selectAll('.entity-name')
89585                     .text(function(d) {
89586                         // fetch latest entity
89587                         var entity = context.entity(d.id);
89588                         return utilDisplayName(entity);
89589                     });
89590             }
89591
89592             return section;
89593         }
89594
89595         function uiEntityEditor(context) {
89596             var dispatch$1 = dispatch('choose');
89597             var _state = 'select';
89598             var _coalesceChanges = false;
89599             var _modified = false;
89600             var _base;
89601             var _entityIDs;
89602             var _activePresets = [];
89603             var _newFeature;
89604
89605             var _sections;
89606
89607             function entityEditor(selection) {
89608
89609                 var combinedTags = utilCombinedTags(_entityIDs, context.graph());
89610
89611                 // Header
89612                 var header = selection.selectAll('.header')
89613                     .data([0]);
89614
89615                 // Enter
89616                 var headerEnter = header.enter()
89617                     .append('div')
89618                     .attr('class', 'header fillL cf');
89619
89620                 headerEnter
89621                     .append('button')
89622                     .attr('class', 'preset-reset preset-choose')
89623                     .call(svgIcon((_mainLocalizer.textDirection() === 'rtl') ? '#iD-icon-forward' : '#iD-icon-backward'));
89624
89625                 headerEnter
89626                     .append('button')
89627                     .attr('class', 'close')
89628                     .on('click', function() { context.enter(modeBrowse(context)); })
89629                     .call(svgIcon(_modified ? '#iD-icon-apply' : '#iD-icon-close'));
89630
89631                 headerEnter
89632                     .append('h3');
89633
89634                 // Update
89635                 header = header
89636                     .merge(headerEnter);
89637
89638                 header.selectAll('h3')
89639                     .text(_entityIDs.length === 1 ? _t('inspector.edit') : _t('inspector.edit_features'));
89640
89641                 header.selectAll('.preset-reset')
89642                     .on('click', function() {
89643                         dispatch$1.call('choose', this, _activePresets);
89644                     });
89645
89646                 // Body
89647                 var body = selection.selectAll('.inspector-body')
89648                     .data([0]);
89649
89650                 // Enter
89651                 var bodyEnter = body.enter()
89652                     .append('div')
89653                     .attr('class', 'entity-editor inspector-body sep-top');
89654
89655                 // Update
89656                 body = body
89657                     .merge(bodyEnter);
89658
89659                 if (!_sections) {
89660                     _sections = [
89661                         uiSectionSelectionList(context),
89662                         uiSectionFeatureType(context).on('choose', function(presets) {
89663                             dispatch$1.call('choose', this, presets);
89664                         }),
89665                         uiSectionEntityIssues(context),
89666                         uiSectionPresetFields(context).on('change', changeTags).on('revert', revertTags),
89667                         uiSectionRawTagEditor('raw-tag-editor', context).on('change', changeTags),
89668                         uiSectionRawMemberEditor(context),
89669                         uiSectionRawMembershipEditor(context)
89670                     ];
89671                 }
89672
89673                 _sections.forEach(function(section) {
89674                     if (section.entityIDs) {
89675                         section.entityIDs(_entityIDs);
89676                     }
89677                     if (section.presets) {
89678                         section.presets(_activePresets);
89679                     }
89680                     if (section.tags) {
89681                         section.tags(combinedTags);
89682                     }
89683                     if (section.state) {
89684                         section.state(_state);
89685                     }
89686                     body.call(section.render);
89687                 });
89688
89689                 body
89690                     .selectAll('.key-trap-wrap')
89691                     .data([0])
89692                     .enter()
89693                     .append('div')
89694                     .attr('class', 'key-trap-wrap')
89695                     .append('input')
89696                     .attr('type', 'text')
89697                     .attr('class', 'key-trap')
89698                     .on('keydown.key-trap', function() {
89699                         // On tabbing, send focus back to the first field on the inspector-body
89700                         // (probably the `name` field) #4159
89701                         if (event.keyCode === 9 && !event.shiftKey) {
89702                             event.preventDefault();
89703                             body.select('input').node().focus();
89704                         }
89705                     });
89706
89707                 context.history()
89708                     .on('change.entity-editor', historyChanged);
89709
89710                 function historyChanged(difference) {
89711                     if (selection.selectAll('.entity-editor').empty()) { return; }
89712                     if (_state === 'hide') { return; }
89713                     var significant = !difference ||
89714                             difference.didChange.properties ||
89715                             difference.didChange.addition ||
89716                             difference.didChange.deletion;
89717                     if (!significant) { return; }
89718
89719                     _entityIDs = _entityIDs.filter(context.hasEntity);
89720                     if (!_entityIDs.length) { return; }
89721
89722                     var priorActivePreset = _activePresets.length === 1 && _activePresets[0];
89723
89724                     loadActivePresets();
89725
89726                     var graph = context.graph();
89727                     entityEditor.modified(_base !== graph);
89728                     entityEditor(selection);
89729
89730                     if (priorActivePreset && _activePresets.length === 1 && priorActivePreset !== _activePresets[0]) {
89731                         // flash the button to indicate the preset changed
89732                         context.container().selectAll('.entity-editor button.preset-reset .label')
89733                             .style('background-color', '#fff')
89734                             .transition()
89735                             .duration(750)
89736                             .style('background-color', null);
89737                     }
89738                 }
89739             }
89740
89741
89742             // Tag changes that fire on input can all get coalesced into a single
89743             // history operation when the user leaves the field.  #2342
89744             // Use explicit entityIDs in case the selection changes before the event is fired.
89745             function changeTags(entityIDs, changed, onInput) {
89746
89747                 var actions = [];
89748                 for (var i in entityIDs) {
89749                     var entityID = entityIDs[i];
89750                     var entity = context.entity(entityID);
89751
89752                     var tags = Object.assign({}, entity.tags);   // shallow copy
89753
89754                     for (var k in changed) {
89755                         if (!k) { continue; }
89756                         var v = changed[k];
89757                         if (v !== undefined || tags.hasOwnProperty(k)) {
89758                             tags[k] = v;
89759                         }
89760                     }
89761
89762                     if (!onInput) {
89763                         tags = utilCleanTags(tags);
89764                     }
89765
89766                     if (!fastDeepEqual(entity.tags, tags)) {
89767                         actions.push(actionChangeTags(entityID, tags));
89768                     }
89769                 }
89770
89771                 if (actions.length) {
89772                     var combinedAction = function(graph) {
89773                         actions.forEach(function(action) {
89774                             graph = action(graph);
89775                         });
89776                         return graph;
89777                     };
89778
89779                     var annotation = _t('operations.change_tags.annotation');
89780
89781                     if (_coalesceChanges) {
89782                         context.overwrite(combinedAction, annotation);
89783                     } else {
89784                         context.perform(combinedAction, annotation);
89785                         _coalesceChanges = !!onInput;
89786                     }
89787                 }
89788
89789                 // if leaving field (blur event), rerun validation
89790                 if (!onInput) {
89791                     context.validator().validate();
89792                 }
89793             }
89794
89795             function revertTags(keys) {
89796
89797                 var actions = [];
89798                 for (var i in _entityIDs) {
89799                     var entityID = _entityIDs[i];
89800
89801                     var original = context.graph().base().entities[entityID];
89802                     var changed = {};
89803                     for (var j in keys) {
89804                         var key = keys[j];
89805                         changed[key] = original ? original.tags[key] : undefined;
89806                     }
89807
89808                     var entity = context.entity(entityID);
89809                     var tags = Object.assign({}, entity.tags);   // shallow copy
89810
89811                     for (var k in changed) {
89812                         if (!k) { continue; }
89813                         var v = changed[k];
89814                         if (v !== undefined || tags.hasOwnProperty(k)) {
89815                             tags[k] = v;
89816                         }
89817                     }
89818
89819
89820                     tags = utilCleanTags(tags);
89821
89822                     if (!fastDeepEqual(entity.tags, tags)) {
89823                         actions.push(actionChangeTags(entityID, tags));
89824                     }
89825
89826                 }
89827
89828                 if (actions.length) {
89829                     var combinedAction = function(graph) {
89830                         actions.forEach(function(action) {
89831                             graph = action(graph);
89832                         });
89833                         return graph;
89834                     };
89835
89836                     var annotation = _t('operations.change_tags.annotation');
89837
89838                     if (_coalesceChanges) {
89839                         context.overwrite(combinedAction, annotation);
89840                     } else {
89841                         context.perform(combinedAction, annotation);
89842                         _coalesceChanges = false;
89843                     }
89844                 }
89845
89846                 context.validator().validate();
89847             }
89848
89849
89850             entityEditor.modified = function(val) {
89851                 if (!arguments.length) { return _modified; }
89852                 _modified = val;
89853                 return entityEditor;
89854             };
89855
89856
89857             entityEditor.state = function(val) {
89858                 if (!arguments.length) { return _state; }
89859                 _state = val;
89860                 return entityEditor;
89861             };
89862
89863
89864             entityEditor.entityIDs = function(val) {
89865                 if (!arguments.length) { return _entityIDs; }
89866                 if (val && _entityIDs && utilArrayIdentical(_entityIDs, val)) { return entityEditor; }  // exit early if no change
89867
89868                 _entityIDs = val;
89869                 _base = context.graph();
89870                 _coalesceChanges = false;
89871
89872                 loadActivePresets(true);
89873
89874                 return entityEditor
89875                     .modified(false);
89876             };
89877
89878
89879             entityEditor.newFeature = function(val) {
89880                 if (!arguments.length) { return _newFeature; }
89881                 _newFeature = val;
89882                 return entityEditor;
89883             };
89884
89885
89886             function loadActivePresets(isForNewSelection) {
89887
89888                 var graph = context.graph();
89889
89890                 var counts = {};
89891
89892                 for (var i in _entityIDs) {
89893                     var entity = graph.hasEntity(_entityIDs[i]);
89894                     if (!entity) { return; }
89895
89896                     var match = _mainPresetIndex.match(entity, graph);
89897
89898                     if (!counts[match.id]) { counts[match.id] = 0; }
89899                     counts[match.id] += 1;
89900                 }
89901
89902                 var matches = Object.keys(counts).sort(function(p1, p2) {
89903                     return counts[p2] - counts[p1];
89904                 }).map(function(pID) {
89905                     return _mainPresetIndex.item(pID);
89906                 });
89907
89908                 if (!isForNewSelection) {
89909                     // A "weak" preset doesn't set any tags. (e.g. "Address")
89910                     var weakPreset = _activePresets.length === 1 &&
89911                         !_activePresets[0].isFallback() &&
89912                         Object.keys(_activePresets[0].addTags || {}).length === 0;
89913                     // Don't replace a weak preset with a fallback preset (e.g. "Point")
89914                     if (weakPreset && matches.length === 1 && matches[0].isFallback()) { return; }
89915                 }
89916
89917                 entityEditor.presets(matches);
89918             }
89919
89920             entityEditor.presets = function(val) {
89921                 if (!arguments.length) { return _activePresets; }
89922
89923                 // don't reload the same preset
89924                 if (!utilArrayIdentical(val, _activePresets)) {
89925                     _activePresets = val;
89926                 }
89927                 return entityEditor;
89928             };
89929
89930             return utilRebind(entityEditor, dispatch$1, 'on');
89931         }
89932
89933         function uiPresetList(context) {
89934             var dispatch$1 = dispatch('cancel', 'choose');
89935             var _entityIDs;
89936             var _currentPresets;
89937             var _autofocus = false;
89938
89939
89940             function presetList(selection) {
89941                 if (!_entityIDs) { return; }
89942
89943                 var presets = _mainPresetIndex.matchAllGeometry(entityGeometries());
89944
89945                 selection.html('');
89946
89947                 var messagewrap = selection
89948                     .append('div')
89949                     .attr('class', 'header fillL');
89950
89951                 var message = messagewrap
89952                     .append('h3')
89953                     .text(_t('inspector.choose'));
89954
89955                 messagewrap
89956                     .append('button')
89957                     .attr('class', 'preset-choose')
89958                     .on('click', function() { dispatch$1.call('cancel', this); })
89959                     .call(svgIcon((_mainLocalizer.textDirection() === 'rtl') ? '#iD-icon-backward' : '#iD-icon-forward'));
89960
89961                 function initialKeydown() {
89962                     // hack to let delete shortcut work when search is autofocused
89963                     if (search.property('value').length === 0 &&
89964                         (event.keyCode === utilKeybinding.keyCodes['⌫'] ||
89965                          event.keyCode === utilKeybinding.keyCodes['⌦'])) {
89966                         event.preventDefault();
89967                         event.stopPropagation();
89968                         operationDelete(context, _entityIDs)();
89969
89970                     // hack to let undo work when search is autofocused
89971                     } else if (search.property('value').length === 0 &&
89972                         (event.ctrlKey || event.metaKey) &&
89973                         event.keyCode === utilKeybinding.keyCodes.z) {
89974                         event.preventDefault();
89975                         event.stopPropagation();
89976                         context.undo();
89977                     } else if (!event.ctrlKey && !event.metaKey) {
89978                         // don't check for delete/undo hack on future keydown events
89979                         select(this).on('keydown', keydown);
89980                         keydown.call(this);
89981                     }
89982                 }
89983
89984                 function keydown() {
89985                     // down arrow
89986                     if (event.keyCode === utilKeybinding.keyCodes['↓'] &&
89987                         // if insertion point is at the end of the string
89988                         search.node().selectionStart === search.property('value').length) {
89989                         event.preventDefault();
89990                         event.stopPropagation();
89991                         // move focus to the first item in the preset list
89992                         var buttons = list.selectAll('.preset-list-button');
89993                         if (!buttons.empty()) { buttons.nodes()[0].focus(); }
89994                     }
89995                 }
89996
89997                 function keypress() {
89998                     // enter
89999                     var value = search.property('value');
90000                     if (event.keyCode === 13 && value.length) {
90001                         list.selectAll('.preset-list-item:first-child')
90002                             .each(function(d) { d.choose.call(this); });
90003                     }
90004                 }
90005
90006                 function inputevent() {
90007                     var value = search.property('value');
90008                     list.classed('filtered', value.length);
90009                     var extent = combinedEntityExtent();
90010                     var results, messageText;
90011                     if (value.length && extent) {
90012                         var center = extent.center();
90013                         var countryCode = iso1A2Code(center);
90014
90015                         results = presets.search(value, entityGeometries()[0], countryCode && countryCode.toLowerCase());
90016                         messageText = _t('inspector.results', {
90017                             n: results.collection.length,
90018                             search: value
90019                         });
90020                     } else {
90021                         results = _mainPresetIndex.defaults(entityGeometries()[0], 36, !context.inIntro());
90022                         messageText = _t('inspector.choose');
90023                     }
90024                     list.call(drawList, results);
90025                     message.text(messageText);
90026                 }
90027
90028                 var searchWrap = selection
90029                     .append('div')
90030                     .attr('class', 'search-header');
90031
90032                 var search = searchWrap
90033                     .append('input')
90034                     .attr('class', 'preset-search-input')
90035                     .attr('placeholder', _t('inspector.search'))
90036                     .attr('type', 'search')
90037                     .call(utilNoAuto)
90038                     .on('keydown', initialKeydown)
90039                     .on('keypress', keypress)
90040                     .on('input', inputevent);
90041
90042                 searchWrap
90043                     .call(svgIcon('#iD-icon-search', 'pre-text'));
90044
90045                 if (_autofocus) {
90046                     search.node().focus();
90047                 }
90048
90049                 var listWrap = selection
90050                     .append('div')
90051                     .attr('class', 'inspector-body');
90052
90053                 var list = listWrap
90054                     .append('div')
90055                     .attr('class', 'preset-list')
90056                     .call(drawList, _mainPresetIndex.defaults(entityGeometries()[0], 36, !context.inIntro()));
90057
90058                 context.features().on('change.preset-list', updateForFeatureHiddenState);
90059             }
90060
90061
90062             function drawList(list, presets) {
90063                 presets = presets.matchAllGeometry(entityGeometries());
90064                 var collection = presets.collection.reduce(function(collection, preset) {
90065                     if (!preset) { return collection; }
90066
90067                     if (preset.members) {
90068                         if (preset.members.collection.filter(function(preset) {
90069                             return preset.addable();
90070                         }).length > 1) {
90071                             collection.push(CategoryItem(preset));
90072                         }
90073                     } else if (preset.addable()) {
90074                         collection.push(PresetItem(preset));
90075                     }
90076                     return collection;
90077                 }, []);
90078
90079                 var items = list.selectAll('.preset-list-item')
90080                     .data(collection, function(d) { return d.preset.id; });
90081
90082                 items.order();
90083
90084                 items.exit()
90085                     .remove();
90086
90087                 items.enter()
90088                     .append('div')
90089                     .attr('class', function(item) { return 'preset-list-item preset-' + item.preset.id.replace('/', '-'); })
90090                     .classed('current', function(item) { return _currentPresets.indexOf(item.preset) !== -1; })
90091                     .each(function(item) { select(this).call(item); })
90092                     .style('opacity', 0)
90093                     .transition()
90094                     .style('opacity', 1);
90095
90096                 updateForFeatureHiddenState();
90097             }
90098
90099             function itemKeydown(){
90100                 // the actively focused item
90101                 var item = select(this.closest('.preset-list-item'));
90102                 var parentItem = select(item.node().parentNode.closest('.preset-list-item'));
90103
90104                 // arrow down, move focus to the next, lower item
90105                 if (event.keyCode === utilKeybinding.keyCodes['↓']) {
90106                     event.preventDefault();
90107                     event.stopPropagation();
90108                     // the next item in the list at the same level
90109                     var nextItem = select(item.node().nextElementSibling);
90110                     // if there is no next item in this list
90111                     if (nextItem.empty()) {
90112                         // if there is a parent item
90113                         if (!parentItem.empty()) {
90114                             // the item is the last item of a sublist,
90115                             // select the next item at the parent level
90116                             nextItem = select(parentItem.node().nextElementSibling);
90117                         }
90118                     // if the focused item is expanded
90119                     } else if (select(this).classed('expanded')) {
90120                         // select the first subitem instead
90121                         nextItem = item.select('.subgrid .preset-list-item:first-child');
90122                     }
90123                     if (!nextItem.empty()) {
90124                         // focus on the next item
90125                         nextItem.select('.preset-list-button').node().focus();
90126                     }
90127
90128                 // arrow up, move focus to the previous, higher item
90129                 } else if (event.keyCode === utilKeybinding.keyCodes['↑']) {
90130                     event.preventDefault();
90131                     event.stopPropagation();
90132                     // the previous item in the list at the same level
90133                     var previousItem = select(item.node().previousElementSibling);
90134
90135                     // if there is no previous item in this list
90136                     if (previousItem.empty()) {
90137                         // if there is a parent item
90138                         if (!parentItem.empty()) {
90139                             // the item is the first subitem of a sublist select the parent item
90140                             previousItem = parentItem;
90141                         }
90142                     // if the previous item is expanded
90143                     } else if (previousItem.select('.preset-list-button').classed('expanded')) {
90144                         // select the last subitem of the sublist of the previous item
90145                         previousItem = previousItem.select('.subgrid .preset-list-item:last-child');
90146                     }
90147
90148                     if (!previousItem.empty()) {
90149                         // focus on the previous item
90150                         previousItem.select('.preset-list-button').node().focus();
90151                     } else {
90152                         // the focus is at the top of the list, move focus back to the search field
90153                         var search = select(this.closest('.preset-list-pane')).select('.preset-search-input');
90154                         search.node().focus();
90155                     }
90156
90157                 // arrow left, move focus to the parent item if there is one
90158                 } else if (event.keyCode === utilKeybinding.keyCodes[(_mainLocalizer.textDirection() === 'rtl') ? '→' : '←']) {
90159                     event.preventDefault();
90160                     event.stopPropagation();
90161                     // if there is a parent item, focus on the parent item
90162                     if (!parentItem.empty()) {
90163                         parentItem.select('.preset-list-button').node().focus();
90164                     }
90165
90166                 // arrow right, choose this item
90167                 } else if (event.keyCode === utilKeybinding.keyCodes[(_mainLocalizer.textDirection() === 'rtl') ? '←' : '→']) {
90168                     event.preventDefault();
90169                     event.stopPropagation();
90170                     item.datum().choose.call(select(this).node());
90171                 }
90172             }
90173
90174
90175             function CategoryItem(preset) {
90176                 var box, sublist, shown = false;
90177
90178                 function item(selection) {
90179                     var wrap = selection.append('div')
90180                         .attr('class', 'preset-list-button-wrap category');
90181
90182                     function click() {
90183                         var isExpanded = select(this).classed('expanded');
90184                         var iconName = isExpanded ?
90185                             (_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward') : '#iD-icon-down';
90186                         select(this)
90187                             .classed('expanded', !isExpanded);
90188                         select(this).selectAll('div.label-inner svg.icon use')
90189                             .attr('href', iconName);
90190                         item.choose();
90191                     }
90192
90193                     var geometries = entityGeometries();
90194
90195                     var button = wrap
90196                         .append('button')
90197                         .attr('class', 'preset-list-button')
90198                         .classed('expanded', false)
90199                         .call(uiPresetIcon()
90200                             .geometry(geometries.length === 1 && geometries[0])
90201                             .preset(preset))
90202                         .on('click', click)
90203                         .on('keydown', function() {
90204                             // right arrow, expand the focused item
90205                             if (event.keyCode === utilKeybinding.keyCodes[(_mainLocalizer.textDirection() === 'rtl') ? '←' : '→']) {
90206                                 event.preventDefault();
90207                                 event.stopPropagation();
90208                                 // if the item isn't expanded
90209                                 if (!select(this).classed('expanded')) {
90210                                     // toggle expansion (expand the item)
90211                                     click.call(this);
90212                                 }
90213                             // left arrow, collapse the focused item
90214                             } else if (event.keyCode === utilKeybinding.keyCodes[(_mainLocalizer.textDirection() === 'rtl') ? '→' : '←']) {
90215                                 event.preventDefault();
90216                                 event.stopPropagation();
90217                                 // if the item is expanded
90218                                 if (select(this).classed('expanded')) {
90219                                     // toggle expansion (collapse the item)
90220                                     click.call(this);
90221                                 }
90222                             } else {
90223                                 itemKeydown.call(this);
90224                             }
90225                         });
90226
90227                     var label = button
90228                         .append('div')
90229                         .attr('class', 'label')
90230                         .append('div')
90231                         .attr('class', 'label-inner');
90232
90233                     label
90234                         .append('div')
90235                         .attr('class', 'namepart')
90236                         .call(svgIcon((_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward'), 'inline'))
90237                         .append('span')
90238                         .html(function() { return preset.name() + '&hellip;'; });
90239
90240                     box = selection.append('div')
90241                         .attr('class', 'subgrid')
90242                         .style('max-height', '0px')
90243                         .style('opacity', 0);
90244
90245                     box.append('div')
90246                         .attr('class', 'arrow');
90247
90248                     sublist = box.append('div')
90249                         .attr('class', 'preset-list fillL3');
90250                 }
90251
90252
90253                 item.choose = function() {
90254                     if (!box || !sublist) { return; }
90255
90256                     if (shown) {
90257                         shown = false;
90258                         box.transition()
90259                             .duration(200)
90260                             .style('opacity', '0')
90261                             .style('max-height', '0px')
90262                             .style('padding-bottom', '0px');
90263                     } else {
90264                         shown = true;
90265                         var members = preset.members.matchAllGeometry(entityGeometries());
90266                         sublist.call(drawList, members);
90267                         box.transition()
90268                             .duration(200)
90269                             .style('opacity', '1')
90270                             .style('max-height', 200 + members.collection.length * 190 + 'px')
90271                             .style('padding-bottom', '10px');
90272                     }
90273                 };
90274
90275                 item.preset = preset;
90276                 return item;
90277             }
90278
90279
90280             function PresetItem(preset) {
90281                 function item(selection) {
90282                     var wrap = selection.append('div')
90283                         .attr('class', 'preset-list-button-wrap');
90284
90285                     var geometries = entityGeometries();
90286
90287                     var button = wrap.append('button')
90288                         .attr('class', 'preset-list-button')
90289                         .call(uiPresetIcon()
90290                             .geometry(geometries.length === 1 && geometries[0])
90291                             .preset(preset))
90292                         .on('click', item.choose)
90293                         .on('keydown', itemKeydown);
90294
90295                     var label = button
90296                         .append('div')
90297                         .attr('class', 'label')
90298                         .append('div')
90299                         .attr('class', 'label-inner');
90300
90301                     // NOTE: split/join on en-dash, not a hypen (to avoid conflict with fr - nl names in Brussels etc)
90302                     label.selectAll('.namepart')
90303                         .data(preset.name().split(' – '))
90304                         .enter()
90305                         .append('div')
90306                         .attr('class', 'namepart')
90307                         .text(function(d) { return d; });
90308
90309                     wrap.call(item.reference.button);
90310                     selection.call(item.reference.body);
90311                 }
90312
90313                 item.choose = function() {
90314                     if (select(this).classed('disabled')) { return; }
90315                     if (!context.inIntro()) {
90316                         _mainPresetIndex.setMostRecent(preset, entityGeometries()[0]);
90317                     }
90318                     context.perform(
90319                         function(graph) {
90320                             for (var i in _entityIDs) {
90321                                 var entityID = _entityIDs[i];
90322                                 var oldPreset = _mainPresetIndex.match(graph.entity(entityID), graph);
90323                                 graph = actionChangePreset(entityID, oldPreset, preset)(graph);
90324                             }
90325                             return graph;
90326                         },
90327                         _t('operations.change_tags.annotation')
90328                     );
90329
90330                     context.validator().validate();  // rerun validation
90331                     dispatch$1.call('choose', this, preset);
90332                 };
90333
90334                 item.help = function() {
90335                     event.stopPropagation();
90336                     item.reference.toggle();
90337                 };
90338
90339                 item.preset = preset;
90340                 item.reference = uiTagReference(preset.reference(entityGeometries()[0]));
90341
90342                 return item;
90343             }
90344
90345
90346             function updateForFeatureHiddenState() {
90347                 if (!_entityIDs.every(context.hasEntity)) { return; }
90348
90349                 var geometries = entityGeometries();
90350                 var button = context.container().selectAll('.preset-list .preset-list-button');
90351
90352                 // remove existing tooltips
90353                 button.call(uiTooltip().destroyAny);
90354
90355                 button.each(function(item, index) {
90356                     var hiddenPresetFeaturesId;
90357                     for (var i in geometries) {
90358                         hiddenPresetFeaturesId = context.features().isHiddenPreset(item.preset, geometries[i]);
90359                         if (hiddenPresetFeaturesId) { break; }
90360                     }
90361                     var isHiddenPreset = !context.inIntro() &&
90362                         !!hiddenPresetFeaturesId &&
90363                         (_currentPresets.length !== 1 || item.preset !== _currentPresets[0]);
90364
90365                     select(this)
90366                         .classed('disabled', isHiddenPreset);
90367
90368                     if (isHiddenPreset) {
90369                         var isAutoHidden = context.features().autoHidden(hiddenPresetFeaturesId);
90370                         var tooltipIdSuffix = isAutoHidden ? 'zoom' : 'manual';
90371                         var tooltipObj = { features: _t('feature.' + hiddenPresetFeaturesId + '.description') };
90372                         select(this).call(uiTooltip()
90373                             .title(_t('inspector.hidden_preset.' + tooltipIdSuffix, tooltipObj))
90374                             .placement(index < 2 ? 'bottom' : 'top')
90375                         );
90376                     }
90377                 });
90378             }
90379
90380             presetList.autofocus = function(val) {
90381                 if (!arguments.length) { return _autofocus; }
90382                 _autofocus = val;
90383                 return presetList;
90384             };
90385
90386             presetList.entityIDs = function(val) {
90387                 if (!arguments.length) { return _entityIDs; }
90388                 _entityIDs = val;
90389                 if (_entityIDs && _entityIDs.length) {
90390                     var presets = _entityIDs.map(function(entityID) {
90391                         return _mainPresetIndex.match(context.entity(entityID), context.graph());
90392                     });
90393                     presetList.presets(presets);
90394                 }
90395                 return presetList;
90396             };
90397
90398             presetList.presets = function(val) {
90399                 if (!arguments.length) { return _currentPresets; }
90400                 _currentPresets = val;
90401                 return presetList;
90402             };
90403
90404             function entityGeometries() {
90405
90406                 var counts = {};
90407
90408                 for (var i in _entityIDs) {
90409                     var entityID = _entityIDs[i];
90410                     var entity = context.entity(entityID);
90411                     var geometry = entity.geometry(context.graph());
90412
90413                     // Treat entities on addr:interpolation lines as points, not vertices (#3241)
90414                     if (geometry === 'vertex' && entity.isOnAddressLine(context.graph())) {
90415                         geometry = 'point';
90416                     }
90417
90418                     if (!counts[geometry]) { counts[geometry] = 0; }
90419                     counts[geometry] += 1;
90420                 }
90421
90422                 return Object.keys(counts).sort(function(geom1, geom2) {
90423                     return counts[geom2] - counts[geom1];
90424                 });
90425             }
90426
90427             function combinedEntityExtent() {
90428                 return _entityIDs.reduce(function(extent, entityID) {
90429                     var entity = context.graph().entity(entityID);
90430                     return extent.extend(entity.extent(context.graph()));
90431                 }, geoExtent());
90432             }
90433
90434             return utilRebind(presetList, dispatch$1, 'on');
90435         }
90436
90437         function uiInspector(context) {
90438             var presetList = uiPresetList(context);
90439             var entityEditor = uiEntityEditor(context);
90440             var wrap = select(null),
90441                 presetPane = select(null),
90442                 editorPane = select(null);
90443             var _state = 'select';
90444             var _entityIDs;
90445             var _newFeature = false;
90446
90447
90448             function inspector(selection) {
90449                 presetList
90450                     .entityIDs(_entityIDs)
90451                     .autofocus(_newFeature)
90452                     .on('choose', inspector.setPreset)
90453                     .on('cancel', function() {
90454                         wrap.transition()
90455                             .styleTween('right', function() { return interpolate('-100%', '0%'); });
90456                         editorPane.call(entityEditor);
90457                     });
90458
90459                 entityEditor
90460                     .state(_state)
90461                     .entityIDs(_entityIDs)
90462                     .on('choose', inspector.showList);
90463
90464                 wrap = selection.selectAll('.panewrap')
90465                     .data([0]);
90466
90467                 var enter = wrap.enter()
90468                     .append('div')
90469                     .attr('class', 'panewrap');
90470
90471                 enter
90472                     .append('div')
90473                     .attr('class', 'preset-list-pane pane');
90474
90475                 enter
90476                     .append('div')
90477                     .attr('class', 'entity-editor-pane pane');
90478
90479                 wrap = wrap.merge(enter);
90480                 presetPane = wrap.selectAll('.preset-list-pane');
90481                 editorPane = wrap.selectAll('.entity-editor-pane');
90482
90483                 function shouldDefaultToPresetList() {
90484                     // always show the inspector on hover
90485                     if (_state !== 'select') { return false; }
90486
90487                     // can only change preset on single selection
90488                     if (_entityIDs.length !== 1) { return false; }
90489
90490                     var entityID = _entityIDs[0];
90491                     var entity = context.hasEntity(entityID);
90492                     if (!entity) { return false; }
90493
90494                     // default to inspector if there are already tags
90495                     if (entity.hasNonGeometryTags()) { return false; }
90496
90497                     // prompt to select preset if feature is new and untagged
90498                     if (_newFeature) { return true; }
90499
90500                     // all existing features except vertices should default to inspector
90501                     if (entity.geometry(context.graph()) !== 'vertex') { return false; }
90502
90503                     // show vertex relations if any
90504                     if (context.graph().parentRelations(entity).length) { return false; }
90505
90506                     // show vertex issues if there are any
90507                     if (context.validator().getEntityIssues(entityID).length) { return false; }
90508
90509                     // show turn retriction editor for junction vertices
90510                     if (entity.isHighwayIntersection(context.graph())) { return false; }
90511
90512                     // otherwise show preset list for uninteresting vertices
90513                     return true;
90514                 }
90515
90516                 if (shouldDefaultToPresetList()) {
90517                     wrap.style('right', '-100%');
90518                     presetPane.call(presetList);
90519                 } else {
90520                     wrap.style('right', '0%');
90521                     editorPane.call(entityEditor);
90522                 }
90523
90524                 var footer = selection.selectAll('.footer')
90525                     .data([0]);
90526
90527                 footer = footer.enter()
90528                     .append('div')
90529                     .attr('class', 'footer')
90530                     .merge(footer);
90531
90532                 footer
90533                     .call(uiViewOnOSM(context)
90534                         .what(context.hasEntity(_entityIDs.length === 1 && _entityIDs[0]))
90535                     );
90536             }
90537
90538             inspector.showList = function(presets) {
90539
90540                 wrap.transition()
90541                     .styleTween('right', function() { return interpolate('0%', '-100%'); });
90542
90543                 if (presets) {
90544                     presetList.presets(presets);
90545                 }
90546
90547                 presetPane
90548                     .call(presetList.autofocus(true));
90549             };
90550
90551             inspector.setPreset = function(preset) {
90552
90553                 // upon setting multipolygon, go to the area preset list instead of the editor
90554                 if (preset.id === 'type/multipolygon') {
90555                     presetPane
90556                         .call(presetList.autofocus(true));
90557
90558                 } else {
90559                     wrap.transition()
90560                         .styleTween('right', function() { return interpolate('-100%', '0%'); });
90561
90562                     editorPane
90563                         .call(entityEditor.presets([preset]));
90564                 }
90565
90566             };
90567
90568             inspector.state = function(val) {
90569                 if (!arguments.length) { return _state; }
90570                 _state = val;
90571                 entityEditor.state(_state);
90572
90573                 // remove any old field help overlay that might have gotten attached to the inspector
90574                 context.container().selectAll('.field-help-body').remove();
90575
90576                 return inspector;
90577             };
90578
90579
90580             inspector.entityIDs = function(val) {
90581                 if (!arguments.length) { return _entityIDs; }
90582                 _entityIDs = val;
90583                 return inspector;
90584             };
90585
90586
90587             inspector.newFeature = function(val) {
90588                 if (!arguments.length) { return _newFeature; }
90589                 _newFeature = val;
90590                 return inspector;
90591             };
90592
90593
90594             return inspector;
90595         }
90596
90597         function uiSidebar(context) {
90598             var inspector = uiInspector(context);
90599             var dataEditor = uiDataEditor(context);
90600             var noteEditor = uiNoteEditor(context);
90601             var improveOsmEditor = uiImproveOsmEditor(context);
90602             var keepRightEditor = uiKeepRightEditor(context);
90603             var osmoseEditor = uiOsmoseEditor(context);
90604             var _current;
90605             var _wasData = false;
90606             var _wasNote = false;
90607             var _wasQaItem = false;
90608
90609             // use pointer events on supported platforms; fallback to mouse events
90610             var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
90611
90612
90613             function sidebar(selection) {
90614                 var container = context.container();
90615                 var minWidth = 240;
90616                 var sidebarWidth;
90617                 var containerWidth;
90618                 var dragOffset;
90619
90620                 // Set the initial width constraints
90621                 selection
90622                     .style('min-width', minWidth + 'px')
90623                     .style('max-width', '400px')
90624                     .style('width', '33.3333%');
90625
90626                 var resizer = selection
90627                     .append('div')
90628                     .attr('class', 'sidebar-resizer')
90629                     .on(_pointerPrefix + 'down.sidebar-resizer', pointerdown);
90630
90631                 var downPointerId, lastClientX, containerLocGetter;
90632
90633                 function pointerdown() {
90634                     if (downPointerId) { return; }
90635
90636                     if ('button' in event && event.button !== 0) { return; }
90637
90638                     downPointerId = event.pointerId || 'mouse';
90639
90640                     lastClientX = event.clientX;
90641
90642                     containerLocGetter = utilFastMouse(container.node());
90643
90644                     // offset from edge of sidebar-resizer
90645                     dragOffset = utilFastMouse(resizer.node())(event)[0] - 1;
90646
90647                     sidebarWidth = selection.node().getBoundingClientRect().width;
90648                     containerWidth = container.node().getBoundingClientRect().width;
90649                     var widthPct = (sidebarWidth / containerWidth) * 100;
90650                     selection
90651                         .style('width', widthPct + '%')    // lock in current width
90652                         .style('max-width', '85%');        // but allow larger widths
90653
90654                     resizer.classed('dragging', true);
90655
90656                     select(window)
90657                         .on('touchmove.sidebar-resizer', function() {
90658                             // disable page scrolling while resizing on touch input
90659                             event.preventDefault();
90660                         }, { passive: false })
90661                         .on(_pointerPrefix + 'move.sidebar-resizer', pointermove)
90662                         .on(_pointerPrefix + 'up.sidebar-resizer pointercancel.sidebar-resizer', pointerup);
90663                 }
90664
90665                 function pointermove() {
90666
90667                     if (downPointerId !== (event.pointerId || 'mouse')) { return; }
90668
90669                     event.preventDefault();
90670
90671                     var dx = event.clientX - lastClientX;
90672
90673                     lastClientX = event.clientX;
90674
90675                     var isRTL = (_mainLocalizer.textDirection() === 'rtl');
90676                     var scaleX = isRTL ? 0 : 1;
90677                     var xMarginProperty = isRTL ? 'margin-right' : 'margin-left';
90678
90679                     var x = containerLocGetter(event)[0] - dragOffset;
90680                     sidebarWidth = isRTL ? containerWidth - x : x;
90681
90682                     var isCollapsed = selection.classed('collapsed');
90683                     var shouldCollapse = sidebarWidth < minWidth;
90684
90685                     selection.classed('collapsed', shouldCollapse);
90686
90687                     if (shouldCollapse) {
90688                         if (!isCollapsed) {
90689                             selection
90690                                 .style(xMarginProperty, '-400px')
90691                                 .style('width', '400px');
90692
90693                             context.ui().onResize([(sidebarWidth - dx) * scaleX, 0]);
90694                         }
90695
90696                     } else {
90697                         var widthPct = (sidebarWidth / containerWidth) * 100;
90698                         selection
90699                             .style(xMarginProperty, null)
90700                             .style('width', widthPct + '%');
90701
90702                         if (isCollapsed) {
90703                             context.ui().onResize([-sidebarWidth * scaleX, 0]);
90704                         } else {
90705                             context.ui().onResize([-dx * scaleX, 0]);
90706                         }
90707                     }
90708                 }
90709
90710                 function pointerup() {
90711                     if (downPointerId !== (event.pointerId || 'mouse')) { return; }
90712
90713                     downPointerId = null;
90714
90715                     resizer.classed('dragging', false);
90716
90717                     select(window)
90718                         .on('touchmove.sidebar-resizer', null)
90719                         .on(_pointerPrefix + 'move.sidebar-resizer', null)
90720                         .on(_pointerPrefix + 'up.sidebar-resizer pointercancel.sidebar-resizer', null);
90721                 }
90722
90723                 var featureListWrap = selection
90724                     .append('div')
90725                     .attr('class', 'feature-list-pane')
90726                     .call(uiFeatureList(context));
90727
90728                 var inspectorWrap = selection
90729                     .append('div')
90730                     .attr('class', 'inspector-hidden inspector-wrap');
90731
90732                 var hoverModeSelect = function(targets) {
90733                     context.container().selectAll('.feature-list-item').classed('hover', false);
90734
90735                     if (context.selectedIDs().length > 1 &&
90736                         targets && targets.length) {
90737
90738                         var elements = context.container().selectAll('.feature-list-item')
90739                             .filter(function (node) {
90740                                 return targets.indexOf(node) !== -1;
90741                             });
90742
90743                         if (!elements.empty()) {
90744                             elements.classed('hover', true);
90745                         }
90746                     }
90747                 };
90748
90749                 sidebar.hoverModeSelect = throttle(hoverModeSelect, 200);
90750
90751                 function hover(targets) {
90752                     var datum = targets && targets.length && targets[0];
90753                     if (datum && datum.__featurehash__) {   // hovering on data
90754                         _wasData = true;
90755                         sidebar
90756                             .show(dataEditor.datum(datum));
90757
90758                         selection.selectAll('.sidebar-component')
90759                             .classed('inspector-hover', true);
90760
90761                     } else if (datum instanceof osmNote) {
90762                         if (context.mode().id === 'drag-note') { return; }
90763                         _wasNote = true;
90764
90765                         var osm = services.osm;
90766                         if (osm) {
90767                             datum = osm.getNote(datum.id);   // marker may contain stale data - get latest
90768                         }
90769
90770                         sidebar
90771                             .show(noteEditor.note(datum));
90772
90773                         selection.selectAll('.sidebar-component')
90774                             .classed('inspector-hover', true);
90775
90776                     } else if (datum instanceof QAItem) {
90777                         _wasQaItem = true;
90778
90779                         var errService = services[datum.service];
90780                         if (errService) {
90781                             // marker may contain stale data - get latest
90782                             datum = errService.getError(datum.id);
90783                         }
90784
90785                         // Currently only three possible services
90786                         var errEditor;
90787                         if (datum.service === 'keepRight') {
90788                             errEditor = keepRightEditor;
90789                         } else if (datum.service === 'osmose') {
90790                             errEditor = osmoseEditor;
90791                         } else {
90792                             errEditor = improveOsmEditor;
90793                         }
90794
90795                         context.container().selectAll('.qaItem.' + datum.service)
90796                             .classed('hover', function(d) { return d.id === datum.id; });
90797
90798                         sidebar
90799                             .show(errEditor.error(datum));
90800
90801                         selection.selectAll('.sidebar-component')
90802                             .classed('inspector-hover', true);
90803
90804                     } else if (!_current && (datum instanceof osmEntity)) {
90805                         featureListWrap
90806                             .classed('inspector-hidden', true);
90807
90808                         inspectorWrap
90809                             .classed('inspector-hidden', false)
90810                             .classed('inspector-hover', true);
90811
90812                         if (!inspector.entityIDs() || !utilArrayIdentical(inspector.entityIDs(), [datum.id]) || inspector.state() !== 'hover') {
90813                             inspector
90814                                 .state('hover')
90815                                 .entityIDs([datum.id])
90816                                 .newFeature(false);
90817
90818                             inspectorWrap
90819                                 .call(inspector);
90820                         }
90821
90822                     } else if (!_current) {
90823                         featureListWrap
90824                             .classed('inspector-hidden', false);
90825                         inspectorWrap
90826                             .classed('inspector-hidden', true);
90827                         inspector
90828                             .state('hide');
90829
90830                     } else if (_wasData || _wasNote || _wasQaItem) {
90831                         _wasNote = false;
90832                         _wasData = false;
90833                         _wasQaItem = false;
90834                         context.container().selectAll('.note').classed('hover', false);
90835                         context.container().selectAll('.qaItem').classed('hover', false);
90836                         sidebar.hide();
90837                     }
90838                 }
90839
90840                 sidebar.hover = throttle(hover, 200);
90841
90842
90843                 sidebar.intersects = function(extent) {
90844                     var rect = selection.node().getBoundingClientRect();
90845                     return extent.intersects([
90846                         context.projection.invert([0, rect.height]),
90847                         context.projection.invert([rect.width, 0])
90848                     ]);
90849                 };
90850
90851
90852                 sidebar.select = function(ids, newFeature) {
90853                     sidebar.hide();
90854
90855                     if (ids && ids.length) {
90856
90857                         var entity = ids.length === 1 && context.entity(ids[0]);
90858                         if (entity && newFeature && selection.classed('collapsed')) {
90859                             // uncollapse the sidebar
90860                             var extent = entity.extent(context.graph());
90861                             sidebar.expand(sidebar.intersects(extent));
90862                         }
90863
90864                         featureListWrap
90865                             .classed('inspector-hidden', true);
90866
90867                         inspectorWrap
90868                             .classed('inspector-hidden', false)
90869                             .classed('inspector-hover', false);
90870
90871                         if (!inspector.entityIDs() || !utilArrayIdentical(inspector.entityIDs(), ids) || inspector.state() !== 'select') {
90872                             inspector
90873                                 .state('select')
90874                                 .entityIDs(ids)
90875                                 .newFeature(newFeature);
90876
90877                             inspectorWrap
90878                                 .call(inspector);
90879                         }
90880
90881                     } else {
90882                         inspector
90883                             .state('hide');
90884                     }
90885                 };
90886
90887
90888                 sidebar.showPresetList = function() {
90889                     inspector.showList();
90890                 };
90891
90892
90893                 sidebar.show = function(component, element) {
90894                     featureListWrap
90895                         .classed('inspector-hidden', true);
90896                     inspectorWrap
90897                         .classed('inspector-hidden', true);
90898
90899                     if (_current) { _current.remove(); }
90900                     _current = selection
90901                         .append('div')
90902                         .attr('class', 'sidebar-component')
90903                         .call(component, element);
90904                 };
90905
90906
90907                 sidebar.hide = function() {
90908                     featureListWrap
90909                         .classed('inspector-hidden', false);
90910                     inspectorWrap
90911                         .classed('inspector-hidden', true);
90912
90913                     if (_current) { _current.remove(); }
90914                     _current = null;
90915                 };
90916
90917
90918                 sidebar.expand = function(moveMap) {
90919                     if (selection.classed('collapsed')) {
90920                         sidebar.toggle(moveMap);
90921                     }
90922                 };
90923
90924
90925                 sidebar.collapse = function(moveMap) {
90926                     if (!selection.classed('collapsed')) {
90927                         sidebar.toggle(moveMap);
90928                     }
90929                 };
90930
90931
90932                 sidebar.toggle = function(moveMap) {
90933                     var e = event;
90934                     if (e && e.sourceEvent) {
90935                         e.sourceEvent.preventDefault();
90936                     } else if (e) {
90937                         e.preventDefault();
90938                     }
90939
90940                     // Don't allow sidebar to toggle when the user is in the walkthrough.
90941                     if (context.inIntro()) { return; }
90942
90943                     var isCollapsed = selection.classed('collapsed');
90944                     var isCollapsing = !isCollapsed;
90945                     var isRTL = (_mainLocalizer.textDirection() === 'rtl');
90946                     var scaleX = isRTL ? 0 : 1;
90947                     var xMarginProperty = isRTL ? 'margin-right' : 'margin-left';
90948
90949                     sidebarWidth = selection.node().getBoundingClientRect().width;
90950
90951                     // switch from % to px
90952                     selection.style('width', sidebarWidth + 'px');
90953
90954                     var startMargin, endMargin, lastMargin;
90955                     if (isCollapsing) {
90956                         startMargin = lastMargin = 0;
90957                         endMargin = -sidebarWidth;
90958                     } else {
90959                         startMargin = lastMargin = -sidebarWidth;
90960                         endMargin = 0;
90961                     }
90962
90963                     selection.transition()
90964                         .style(xMarginProperty, endMargin + 'px')
90965                         .tween('panner', function() {
90966                             var i = d3_interpolateNumber(startMargin, endMargin);
90967                             return function(t) {
90968                                 var dx = lastMargin - Math.round(i(t));
90969                                 lastMargin = lastMargin - dx;
90970                                 context.ui().onResize(moveMap ? undefined : [dx * scaleX, 0]);
90971                             };
90972                         })
90973                         .on('end', function() {
90974                             selection.classed('collapsed', isCollapsing);
90975
90976                             // switch back from px to %
90977                             if (!isCollapsing) {
90978                                 var containerWidth = container.node().getBoundingClientRect().width;
90979                                 var widthPct = (sidebarWidth / containerWidth) * 100;
90980                                 selection
90981                                     .style(xMarginProperty, null)
90982                                     .style('width', widthPct + '%');
90983                             }
90984                         });
90985                 };
90986
90987                 // toggle the sidebar collapse when double-clicking the resizer
90988                 resizer.on('dblclick', sidebar.toggle);
90989
90990                 // ensure hover sidebar is closed when zooming out beyond editable zoom
90991                 context.map().on('crossEditableZoom.sidebar', function(within) {
90992                     if (!within && !selection.select('.inspector-hover').empty()) {
90993                         hover([]);
90994                     }
90995                 });
90996             }
90997
90998             sidebar.showPresetList = function() {};
90999             sidebar.hover = function() {};
91000             sidebar.hover.cancel = function() {};
91001             sidebar.intersects = function() {};
91002             sidebar.select = function() {};
91003             sidebar.show = function() {};
91004             sidebar.hide = function() {};
91005             sidebar.expand = function() {};
91006             sidebar.collapse = function() {};
91007             sidebar.toggle = function() {};
91008
91009             return sidebar;
91010         }
91011
91012         function uiSourceSwitch(context) {
91013             var keys;
91014
91015
91016             function click() {
91017                 event.preventDefault();
91018
91019                 var osm = context.connection();
91020                 if (!osm) { return; }
91021
91022                 if (context.inIntro()) { return; }
91023
91024                 if (context.history().hasChanges() &&
91025                     !window.confirm(_t('source_switch.lose_changes'))) { return; }
91026
91027                 var isLive = select(this)
91028                     .classed('live');
91029
91030                 isLive = !isLive;
91031                 context.enter(modeBrowse(context));
91032                 context.history().clearSaved();          // remove saved history
91033                 context.flush();                         // remove stored data
91034
91035                 select(this)
91036                     .text(isLive ? _t('source_switch.live') : _t('source_switch.dev'))
91037                     .classed('live', isLive)
91038                     .classed('chip', isLive);
91039
91040                 osm.switch(isLive ? keys[0] : keys[1]);  // switch connection (warning: dispatches 'change' event)
91041             }
91042
91043             var sourceSwitch = function(selection) {
91044                 selection
91045                     .append('a')
91046                     .attr('href', '#')
91047                     .text(_t('source_switch.live'))
91048                     .attr('class', 'live chip')
91049                     .on('click', click);
91050             };
91051
91052
91053             sourceSwitch.keys = function(_) {
91054                 if (!arguments.length) { return keys; }
91055                 keys = _;
91056                 return sourceSwitch;
91057             };
91058
91059
91060             return sourceSwitch;
91061         }
91062
91063         function uiSpinner(context) {
91064             var osm = context.connection();
91065
91066
91067             return function(selection) {
91068                 var img = selection
91069                     .append('img')
91070                     .attr('src', context.imagePath('loader-black.gif'))
91071                     .style('opacity', 0);
91072
91073                 if (osm) {
91074                     osm
91075                         .on('loading.spinner', function() {
91076                             img.transition()
91077                                 .style('opacity', 1);
91078                         })
91079                         .on('loaded.spinner', function() {
91080                             img.transition()
91081                                 .style('opacity', 0);
91082                         });
91083                 }
91084             };
91085         }
91086
91087         function uiSplash(context) {
91088           return function (selection) {
91089             // Exception - if there are restorable changes, skip this splash screen.
91090             // This is because we currently only support one `uiModal` at a time
91091             //  and we need to show them `uiRestore`` instead of this one.
91092             if (context.history().hasRestorableChanges()) { return; }
91093
91094             // If user has not seen this version of the privacy policy, show the splash again.
91095             var updateMessage = '';
91096             var sawPrivacyVersion = corePreferences('sawPrivacyVersion');
91097             var showSplash = !corePreferences('sawSplash');
91098             if (sawPrivacyVersion !== context.privacyVersion) {
91099               updateMessage = _t('splash.privacy_update');
91100               showSplash = true;
91101             }
91102
91103             if (!showSplash) { return; }
91104
91105             corePreferences('sawSplash', true);
91106             corePreferences('sawPrivacyVersion', context.privacyVersion);
91107
91108             // fetch intro graph data now, while user is looking at the splash screen
91109             _mainFileFetcher.get('intro_graph');
91110
91111             var modalSelection = uiModal(selection);
91112
91113             modalSelection.select('.modal')
91114               .attr('class', 'modal-splash modal');
91115
91116             var introModal = modalSelection.select('.content')
91117               .append('div')
91118               .attr('class', 'fillL');
91119
91120             introModal
91121               .append('div')
91122               .attr('class','modal-section')
91123               .append('h3')
91124               .text(_t('splash.welcome'));
91125
91126             var modalSection = introModal
91127               .append('div')
91128               .attr('class','modal-section');
91129
91130             modalSection
91131               .append('p')
91132               .html(_t('splash.text', {
91133                 version: context.version,
91134                 website: '<a target="_blank" href="http://ideditor.blog/">ideditor.blog</a>',
91135                 github: '<a target="_blank" href="https://github.com/openstreetmap/iD">github.com</a>'
91136               }));
91137
91138             modalSection
91139               .append('p')
91140               .html(_t('splash.privacy', {
91141                 updateMessage: updateMessage,
91142                 privacyLink: '<a target="_blank" href="https://github.com/openstreetmap/iD/blob/release/PRIVACY.md">' +
91143                   _t('splash.privacy_policy') + '</a>'
91144               }));
91145
91146             var buttonWrap = introModal
91147               .append('div')
91148               .attr('class', 'modal-actions');
91149
91150             var walkthrough = buttonWrap
91151               .append('button')
91152               .attr('class', 'walkthrough')
91153               .on('click', function () {
91154                 context.container().call(uiIntro(context));
91155                 modalSelection.close();
91156               });
91157
91158             walkthrough
91159               .append('svg')
91160               .attr('class', 'logo logo-walkthrough')
91161               .append('use')
91162               .attr('xlink:href', '#iD-logo-walkthrough');
91163
91164             walkthrough
91165               .append('div')
91166               .text(_t('splash.walkthrough'));
91167
91168             var startEditing = buttonWrap
91169               .append('button')
91170               .attr('class', 'start-editing')
91171               .on('click', modalSelection.close);
91172
91173             startEditing
91174               .append('svg')
91175               .attr('class', 'logo logo-features')
91176               .append('use')
91177               .attr('xlink:href', '#iD-logo-features');
91178
91179             startEditing
91180               .append('div')
91181               .text(_t('splash.start'));
91182
91183             modalSelection.select('button.close')
91184               .attr('class','hide');
91185           };
91186         }
91187
91188         function uiStatus(context) {
91189             var osm = context.connection();
91190
91191
91192             return function(selection) {
91193                 if (!osm) { return; }
91194
91195                 function update(err, apiStatus) {
91196                     selection.html('');
91197
91198                     if (err) {
91199                         if (apiStatus === 'connectionSwitched') {
91200                             // if the connection was just switched, we can't rely on
91201                             // the status (we're getting the status of the previous api)
91202                             return;
91203
91204                         } else if (apiStatus === 'rateLimited') {
91205                             selection
91206                                 .text(_t('osm_api_status.message.rateLimit'))
91207                                 .append('a')
91208                                 .attr('class', 'api-status-login')
91209                                 .attr('target', '_blank')
91210                                 .call(svgIcon('#iD-icon-out-link', 'inline'))
91211                                 .append('span')
91212                                 .text(_t('login'))
91213                                 .on('click.login', function() {
91214                                     event.preventDefault();
91215                                     osm.authenticate();
91216                                 });
91217                         } else {
91218
91219                             // don't allow retrying too rapidly
91220                             var throttledRetry = throttle(function() {
91221                                 // try loading the visible tiles
91222                                 context.loadTiles(context.projection);
91223                                 // manually reload the status too in case all visible tiles were already loaded
91224                                 osm.reloadApiStatus();
91225                             }, 2000);
91226
91227                             // eslint-disable-next-line no-warning-comments
91228                             // TODO: nice messages for different error types
91229                             selection
91230                                 .text(_t('osm_api_status.message.error') + ' ')
91231                                 .append('a')
91232                                 // let the user manually retry their connection directly
91233                                 .text(_t('osm_api_status.retry'))
91234                                 .on('click.retry', function() {
91235                                     event.preventDefault();
91236                                     throttledRetry();
91237                                 });
91238                         }
91239
91240                     } else if (apiStatus === 'readonly') {
91241                         selection.text(_t('osm_api_status.message.readonly'));
91242                     } else if (apiStatus === 'offline') {
91243                         selection.text(_t('osm_api_status.message.offline'));
91244                     }
91245
91246                     selection.attr('class', 'api-status ' + (err ? 'error' : apiStatus));
91247                 }
91248
91249                 osm.on('apiStatusChange.uiStatus', update);
91250
91251                 // reload the status periodically regardless of other factors
91252                 window.setInterval(function() {
91253                     osm.reloadApiStatus();
91254                 }, 90000);
91255
91256                 // load the initial status in case no OSM data was loaded yet
91257                 osm.reloadApiStatus();
91258             };
91259         }
91260
91261         function modeDrawArea(context, wayID, startGraph, button) {
91262             var mode = {
91263                 button: button,
91264                 id: 'draw-area'
91265             };
91266
91267             var behavior = behaviorDrawWay(context, wayID, mode, startGraph)
91268                 .on('rejectedSelfIntersection.modeDrawArea', function() {
91269                     context.ui().flash
91270                         .text(_t('self_intersection.error.areas'))();
91271                 });
91272
91273             mode.wayID = wayID;
91274
91275             mode.enter = function() {
91276                 context.install(behavior);
91277             };
91278
91279             mode.exit = function() {
91280                 context.uninstall(behavior);
91281             };
91282
91283             mode.selectedIDs = function() {
91284                 return [wayID];
91285             };
91286
91287             mode.activeID = function() {
91288                 return (behavior && behavior.activeID()) || [];
91289             };
91290
91291             return mode;
91292         }
91293
91294         function modeAddArea(context, mode) {
91295             mode.id = 'add-area';
91296
91297             var behavior = behaviorAddWay(context)
91298                 .on('start', start)
91299                 .on('startFromWay', startFromWay)
91300                 .on('startFromNode', startFromNode);
91301
91302             var defaultTags = { area: 'yes' };
91303             if (mode.preset) { defaultTags = mode.preset.setTags(defaultTags, 'area'); }
91304
91305
91306             function actionClose(wayId) {
91307                 return function (graph) {
91308                     return graph.replace(graph.entity(wayId).close());
91309                 };
91310             }
91311
91312
91313             function start(loc) {
91314                 var startGraph = context.graph();
91315                 var node = osmNode({ loc: loc });
91316                 var way = osmWay({ tags: defaultTags });
91317
91318                 context.perform(
91319                     actionAddEntity(node),
91320                     actionAddEntity(way),
91321                     actionAddVertex(way.id, node.id),
91322                     actionClose(way.id)
91323                 );
91324
91325                 context.enter(modeDrawArea(context, way.id, startGraph, mode.button));
91326             }
91327
91328
91329             function startFromWay(loc, edge) {
91330                 var startGraph = context.graph();
91331                 var node = osmNode({ loc: loc });
91332                 var way = osmWay({ tags: defaultTags });
91333
91334                 context.perform(
91335                     actionAddEntity(node),
91336                     actionAddEntity(way),
91337                     actionAddVertex(way.id, node.id),
91338                     actionClose(way.id),
91339                     actionAddMidpoint({ loc: loc, edge: edge }, node)
91340                 );
91341
91342                 context.enter(modeDrawArea(context, way.id, startGraph, mode.button));
91343             }
91344
91345
91346             function startFromNode(node) {
91347                 var startGraph = context.graph();
91348                 var way = osmWay({ tags: defaultTags });
91349
91350                 context.perform(
91351                     actionAddEntity(way),
91352                     actionAddVertex(way.id, node.id),
91353                     actionClose(way.id)
91354                 );
91355
91356                 context.enter(modeDrawArea(context, way.id, startGraph, mode.button));
91357             }
91358
91359
91360             mode.enter = function() {
91361                 context.install(behavior);
91362             };
91363
91364
91365             mode.exit = function() {
91366                 context.uninstall(behavior);
91367             };
91368
91369
91370             return mode;
91371         }
91372
91373         function modeAddLine(context, mode) {
91374             mode.id = 'add-line';
91375
91376             var behavior = behaviorAddWay(context)
91377                 .on('start', start)
91378                 .on('startFromWay', startFromWay)
91379                 .on('startFromNode', startFromNode);
91380
91381             var defaultTags = {};
91382             if (mode.preset) { defaultTags = mode.preset.setTags(defaultTags, 'line'); }
91383
91384
91385             function start(loc) {
91386                 var startGraph = context.graph();
91387                 var node = osmNode({ loc: loc });
91388                 var way = osmWay({ tags: defaultTags });
91389
91390                 context.perform(
91391                     actionAddEntity(node),
91392                     actionAddEntity(way),
91393                     actionAddVertex(way.id, node.id)
91394                 );
91395
91396                 context.enter(modeDrawLine(context, way.id, startGraph, mode.button));
91397             }
91398
91399
91400             function startFromWay(loc, edge) {
91401                 var startGraph = context.graph();
91402                 var node = osmNode({ loc: loc });
91403                 var way = osmWay({ tags: defaultTags });
91404
91405                 context.perform(
91406                     actionAddEntity(node),
91407                     actionAddEntity(way),
91408                     actionAddVertex(way.id, node.id),
91409                     actionAddMidpoint({ loc: loc, edge: edge }, node)
91410                 );
91411
91412                 context.enter(modeDrawLine(context, way.id, startGraph, mode.button));
91413             }
91414
91415
91416             function startFromNode(node) {
91417                 var startGraph = context.graph();
91418                 var way = osmWay({ tags: defaultTags });
91419
91420                 context.perform(
91421                     actionAddEntity(way),
91422                     actionAddVertex(way.id, node.id)
91423                 );
91424
91425                 context.enter(modeDrawLine(context, way.id, startGraph, mode.button));
91426             }
91427
91428
91429             mode.enter = function() {
91430                 context.install(behavior);
91431             };
91432
91433
91434             mode.exit = function() {
91435                 context.uninstall(behavior);
91436             };
91437
91438             return mode;
91439         }
91440
91441         function modeAddPoint(context, mode) {
91442
91443             mode.id = 'add-point';
91444
91445             var behavior = behaviorDraw(context)
91446                 .on('click', add)
91447                 .on('clickWay', addWay)
91448                 .on('clickNode', addNode)
91449                 .on('cancel', cancel)
91450                 .on('finish', cancel);
91451
91452             var defaultTags = {};
91453             if (mode.preset) { defaultTags = mode.preset.setTags(defaultTags, 'point'); }
91454
91455
91456             function add(loc) {
91457                 var node = osmNode({ loc: loc, tags: defaultTags });
91458
91459                 context.perform(
91460                     actionAddEntity(node),
91461                     _t('operations.add.annotation.point')
91462                 );
91463
91464                 enterSelectMode(node);
91465             }
91466
91467
91468             function addWay(loc, edge) {
91469                 var node = osmNode({ tags: defaultTags });
91470
91471                 context.perform(
91472                     actionAddMidpoint({loc: loc, edge: edge}, node),
91473                     _t('operations.add.annotation.vertex')
91474                 );
91475
91476                 enterSelectMode(node);
91477             }
91478
91479             function enterSelectMode(node) {
91480                 context.enter(
91481                     modeSelect(context, [node.id]).newFeature(true)
91482                 );
91483             }
91484
91485
91486             function addNode(node) {
91487                 if (Object.keys(defaultTags).length === 0) {
91488                     enterSelectMode(node);
91489                     return;
91490                 }
91491
91492                 var tags = Object.assign({}, node.tags);  // shallow copy
91493                 for (var key in defaultTags) {
91494                     tags[key] = defaultTags[key];
91495                 }
91496
91497                 context.perform(
91498                     actionChangeTags(node.id, tags),
91499                     _t('operations.add.annotation.point')
91500                 );
91501
91502                 enterSelectMode(node);
91503             }
91504
91505
91506             function cancel() {
91507                 context.enter(modeBrowse(context));
91508             }
91509
91510
91511             mode.enter = function() {
91512                 context.install(behavior);
91513             };
91514
91515
91516             mode.exit = function() {
91517                 context.uninstall(behavior);
91518             };
91519
91520
91521             return mode;
91522         }
91523
91524         function modeAddNote(context) {
91525             var mode = {
91526                 id: 'add-note',
91527                 button: 'note',
91528                 title: _t('modes.add_note.title'),
91529                 description: _t('modes.add_note.description'),
91530                 key: _t('modes.add_note.key')
91531             };
91532
91533             var behavior = behaviorDraw(context)
91534                 .on('click', add)
91535                 .on('cancel', cancel)
91536                 .on('finish', cancel);
91537
91538
91539             function add(loc) {
91540                 var osm = services.osm;
91541                 if (!osm) { return; }
91542
91543                 var note = osmNote({ loc: loc, status: 'open', comments: [] });
91544                 osm.replaceNote(note);
91545
91546                 // force a reraw (there is no history change that would otherwise do this)
91547                 context.map().pan([0,0]);
91548
91549                 context
91550                     .selectedNoteID(note.id)
91551                     .enter(modeSelectNote(context, note.id).newFeature(true));
91552             }
91553
91554
91555             function cancel() {
91556                 context.enter(modeBrowse(context));
91557             }
91558
91559
91560             mode.enter = function() {
91561                 context.install(behavior);
91562             };
91563
91564
91565             mode.exit = function() {
91566                 context.uninstall(behavior);
91567             };
91568
91569
91570             return mode;
91571         }
91572
91573         function uiConflicts(context) {
91574             var dispatch$1 = dispatch('cancel', 'save');
91575             var keybinding = utilKeybinding('conflicts');
91576             var _origChanges;
91577             var _conflictList;
91578             var _shownConflictIndex;
91579
91580
91581             function keybindingOn() {
91582                 select(document)
91583                     .call(keybinding.on('⎋', cancel, true));
91584             }
91585
91586             function keybindingOff() {
91587                 select(document)
91588                     .call(keybinding.unbind);
91589             }
91590
91591             function tryAgain() {
91592                 keybindingOff();
91593                 dispatch$1.call('save');
91594             }
91595
91596             function cancel() {
91597                 keybindingOff();
91598                 dispatch$1.call('cancel');
91599             }
91600
91601
91602             function conflicts(selection) {
91603                 keybindingOn();
91604
91605                 var headerEnter = selection.selectAll('.header')
91606                     .data([0])
91607                     .enter()
91608                     .append('div')
91609                     .attr('class', 'header fillL');
91610
91611                 headerEnter
91612                     .append('button')
91613                     .attr('class', 'fr')
91614                     .on('click', cancel)
91615                     .call(svgIcon('#iD-icon-close'));
91616
91617                 headerEnter
91618                     .append('h3')
91619                     .text(_t('save.conflict.header'));
91620
91621                 var bodyEnter = selection.selectAll('.body')
91622                     .data([0])
91623                     .enter()
91624                     .append('div')
91625                     .attr('class', 'body fillL');
91626
91627                 var conflictsHelpEnter = bodyEnter
91628                     .append('div')
91629                     .attr('class', 'conflicts-help')
91630                     .text(_t('save.conflict.help'));
91631
91632
91633                 // Download changes link
91634                 var detected = utilDetect();
91635                 var changeset = new osmChangeset();
91636
91637                 delete changeset.id;  // Export without changeset_id
91638
91639                 var data = JXON.stringify(changeset.osmChangeJXON(_origChanges));
91640                 var blob = new Blob([data], { type: 'text/xml;charset=utf-8;' });
91641                 var fileName = 'changes.osc';
91642
91643                 var linkEnter = conflictsHelpEnter.selectAll('.download-changes')
91644                     .append('a')
91645                     .attr('class', 'download-changes');
91646
91647                 if (detected.download) {      // All except IE11 and Edge
91648                     linkEnter                 // download the data as a file
91649                         .attr('href', window.URL.createObjectURL(blob))
91650                         .attr('download', fileName);
91651
91652                 } else {                      // IE11 and Edge
91653                     linkEnter                 // open data uri in a new tab
91654                         .attr('target', '_blank')
91655                         .on('click.download', function() {
91656                             navigator.msSaveBlob(blob, fileName);
91657                         });
91658                 }
91659
91660                 linkEnter
91661                     .call(svgIcon('#iD-icon-load', 'inline'))
91662                     .append('span')
91663                     .text(_t('save.conflict.download_changes'));
91664
91665
91666                 bodyEnter
91667                     .append('div')
91668                     .attr('class', 'conflict-container fillL3')
91669                     .call(showConflict, 0);
91670
91671                 bodyEnter
91672                     .append('div')
91673                     .attr('class', 'conflicts-done')
91674                     .attr('opacity', 0)
91675                     .style('display', 'none')
91676                     .text(_t('save.conflict.done'));
91677
91678                 var buttonsEnter = bodyEnter
91679                     .append('div')
91680                     .attr('class','buttons col12 joined conflicts-buttons');
91681
91682                 buttonsEnter
91683                     .append('button')
91684                     .attr('disabled', _conflictList.length > 1)
91685                     .attr('class', 'action conflicts-button col6')
91686                     .text(_t('save.title'))
91687                     .on('click.try_again', tryAgain);
91688
91689                 buttonsEnter
91690                     .append('button')
91691                     .attr('class', 'secondary-action conflicts-button col6')
91692                     .text(_t('confirm.cancel'))
91693                     .on('click.cancel', cancel);
91694             }
91695
91696
91697             function showConflict(selection, index) {
91698                 index = utilWrap(index, _conflictList.length);
91699                 _shownConflictIndex = index;
91700
91701                 var parent = select(selection.node().parentNode);
91702
91703                 // enable save button if this is the last conflict being reviewed..
91704                 if (index === _conflictList.length - 1) {
91705                     window.setTimeout(function() {
91706                         parent.select('.conflicts-button')
91707                             .attr('disabled', null);
91708
91709                         parent.select('.conflicts-done')
91710                             .transition()
91711                             .attr('opacity', 1)
91712                             .style('display', 'block');
91713                     }, 250);
91714                 }
91715
91716                 var conflict = selection
91717                     .selectAll('.conflict')
91718                     .data([_conflictList[index]]);
91719
91720                 conflict.exit()
91721                     .remove();
91722
91723                 var conflictEnter = conflict.enter()
91724                     .append('div')
91725                     .attr('class', 'conflict');
91726
91727                 conflictEnter
91728                     .append('h4')
91729                     .attr('class', 'conflict-count')
91730                     .text(_t('save.conflict.count', { num: index + 1, total: _conflictList.length }));
91731
91732                 conflictEnter
91733                     .append('a')
91734                     .attr('class', 'conflict-description')
91735                     .attr('href', '#')
91736                     .text(function(d) { return d.name; })
91737                     .on('click', function(d) {
91738                         event.preventDefault();
91739                         zoomToEntity(d.id);
91740                     });
91741
91742                 var details = conflictEnter
91743                     .append('div')
91744                     .attr('class', 'conflict-detail-container');
91745
91746                 details
91747                     .append('ul')
91748                     .attr('class', 'conflict-detail-list')
91749                     .selectAll('li')
91750                     .data(function(d) { return d.details || []; })
91751                     .enter()
91752                     .append('li')
91753                     .attr('class', 'conflict-detail-item')
91754                     .html(function(d) { return d; });
91755
91756                 details
91757                     .append('div')
91758                     .attr('class', 'conflict-choices')
91759                     .call(addChoices);
91760
91761                 details
91762                     .append('div')
91763                     .attr('class', 'conflict-nav-buttons joined cf')
91764                     .selectAll('button')
91765                     .data(['previous', 'next'])
91766                     .enter()
91767                     .append('button')
91768                     .text(function(d) { return _t('save.conflict.' + d); })
91769                     .attr('class', 'conflict-nav-button action col6')
91770                     .attr('disabled', function(d, i) {
91771                         return (i === 0 && index === 0) ||
91772                             (i === 1 && index === _conflictList.length - 1) || null;
91773                     })
91774                     .on('click', function(d, i) {
91775                         event.preventDefault();
91776
91777                         var container = parent.selectAll('.conflict-container');
91778                         var sign = (i === 0 ? -1 : 1);
91779
91780                         container
91781                             .selectAll('.conflict')
91782                             .remove();
91783
91784                         container
91785                             .call(showConflict, index + sign);
91786                     });
91787
91788             }
91789
91790
91791             function addChoices(selection) {
91792                 var choices = selection
91793                     .append('ul')
91794                     .attr('class', 'layer-list')
91795                     .selectAll('li')
91796                     .data(function(d) { return d.choices || []; });
91797
91798                 // enter
91799                 var choicesEnter = choices.enter()
91800                     .append('li')
91801                     .attr('class', 'layer');
91802
91803                 var labelEnter = choicesEnter
91804                     .append('label');
91805
91806                 labelEnter
91807                     .append('input')
91808                     .attr('type', 'radio')
91809                     .attr('name', function(d) { return d.id; })
91810                     .on('change', function(d, i) {
91811                         var ul = this.parentNode.parentNode.parentNode;
91812                         ul.__data__.chosen = i;
91813                         choose(ul, d);
91814                     });
91815
91816                 labelEnter
91817                     .append('span')
91818                     .text(function(d) { return d.text; });
91819
91820                 // update
91821                 choicesEnter
91822                     .merge(choices)
91823                     .each(function(d, i) {
91824                         var ul = this.parentNode;
91825                         if (ul.__data__.chosen === i) {
91826                             choose(ul, d);
91827                         }
91828                     });
91829             }
91830
91831
91832             function choose(ul, datum) {
91833                 if (event) { event.preventDefault(); }
91834
91835                 select(ul)
91836                     .selectAll('li')
91837                     .classed('active', function(d) { return d === datum; })
91838                     .selectAll('input')
91839                     .property('checked', function(d) { return d === datum; });
91840
91841                 var extent = geoExtent();
91842                 var entity;
91843
91844                 entity = context.graph().hasEntity(datum.id);
91845                 if (entity) { extent._extend(entity.extent(context.graph())); }
91846
91847                 datum.action();
91848
91849                 entity = context.graph().hasEntity(datum.id);
91850                 if (entity) { extent._extend(entity.extent(context.graph())); }
91851
91852                 zoomToEntity(datum.id, extent);
91853             }
91854
91855
91856             function zoomToEntity(id, extent) {
91857                 context.surface().selectAll('.hover')
91858                     .classed('hover', false);
91859
91860                 var entity = context.graph().hasEntity(id);
91861                 if (entity) {
91862                     if (extent) {
91863                         context.map().trimmedExtent(extent);
91864                     } else {
91865                         context.map().zoomToEase(entity);
91866                     }
91867                     context.surface().selectAll(utilEntityOrMemberSelector([entity.id], context.graph()))
91868                         .classed('hover', true);
91869                 }
91870             }
91871
91872
91873             // The conflict list should be an array of objects like:
91874             // {
91875             //     id: id,
91876             //     name: entityName(local),
91877             //     details: merge.conflicts(),
91878             //     chosen: 1,
91879             //     choices: [
91880             //         choice(id, keepMine, forceLocal),
91881             //         choice(id, keepTheirs, forceRemote)
91882             //     ]
91883             // }
91884             conflicts.conflictList = function(_) {
91885                 if (!arguments.length) { return _conflictList; }
91886                 _conflictList = _;
91887                 return conflicts;
91888             };
91889
91890
91891             conflicts.origChanges = function(_) {
91892                 if (!arguments.length) { return _origChanges; }
91893                 _origChanges = _;
91894                 return conflicts;
91895             };
91896
91897
91898             conflicts.shownEntityIds = function() {
91899                 if (_conflictList && typeof _shownConflictIndex === 'number') {
91900                     return [_conflictList[_shownConflictIndex].id];
91901                 }
91902                 return [];
91903             };
91904
91905
91906             return utilRebind(conflicts, dispatch$1, 'on');
91907         }
91908
91909         function uiConfirm(selection) {
91910             var modalSelection = uiModal(selection);
91911
91912             modalSelection.select('.modal')
91913                 .classed('modal-alert', true);
91914
91915             var section = modalSelection.select('.content');
91916
91917             section.append('div')
91918                 .attr('class', 'modal-section header');
91919
91920             section.append('div')
91921                 .attr('class', 'modal-section message-text');
91922
91923             var buttons = section.append('div')
91924                 .attr('class', 'modal-section buttons cf');
91925
91926
91927             modalSelection.okButton = function() {
91928                 buttons
91929                     .append('button')
91930                     .attr('class', 'button ok-button action')
91931                     .on('click.confirm', function() {
91932                         modalSelection.remove();
91933                     })
91934                     .text(_t('confirm.okay'))
91935                     .node()
91936                     .focus();
91937
91938                 return modalSelection;
91939             };
91940
91941
91942             return modalSelection;
91943         }
91944
91945         function uiChangesetEditor(context) {
91946             var dispatch$1 = dispatch('change');
91947             var formFields = uiFormFields(context);
91948             var commentCombo = uiCombobox(context, 'comment').caseSensitive(true);
91949             var _fieldsArr;
91950             var _tags;
91951             var _changesetID;
91952
91953
91954             function changesetEditor(selection) {
91955                 render(selection);
91956             }
91957
91958
91959             function render(selection) {
91960                 var initial = false;
91961
91962                 if (!_fieldsArr) {
91963                     initial = true;
91964                     var presets = _mainPresetIndex;
91965
91966                     _fieldsArr = [
91967                         uiField(context, presets.field('comment'), null, { show: true, revert: false }),
91968                         uiField(context, presets.field('source'), null, { show: false, revert: false }),
91969                         uiField(context, presets.field('hashtags'), null, { show: false, revert: false }) ];
91970
91971                     _fieldsArr.forEach(function(field) {
91972                         field
91973                             .on('change', function(t, onInput) {
91974                                 dispatch$1.call('change', field, undefined, t, onInput);
91975                             });
91976                     });
91977                 }
91978
91979                 _fieldsArr.forEach(function(field) {
91980                     field
91981                         .tags(_tags);
91982                 });
91983
91984
91985                 selection
91986                     .call(formFields.fieldsArr(_fieldsArr));
91987
91988
91989                 if (initial) {
91990                     var commentField = selection.select('.form-field-comment textarea');
91991                     var commentNode = commentField.node();
91992
91993                     if (commentNode) {
91994                         commentNode.focus();
91995                         commentNode.select();
91996                     }
91997
91998                     // trigger a 'blur' event so that comment field can be cleaned
91999                     // and checked for hashtags, even if retrieved from localstorage
92000                     utilTriggerEvent(commentField, 'blur');
92001
92002                     var osm = context.connection();
92003                     if (osm) {
92004                         osm.userChangesets(function (err, changesets) {
92005                             if (err) { return; }
92006
92007                             var comments = changesets.map(function(changeset) {
92008                                 var comment = changeset.tags.comment;
92009                                 return comment ? { title: comment, value: comment } : null;
92010                             }).filter(Boolean);
92011
92012                             commentField
92013                                 .call(commentCombo
92014                                     .data(utilArrayUniqBy(comments, 'title'))
92015                                 );
92016                         });
92017                     }
92018                 }
92019
92020                 // Add warning if comment mentions Google
92021                 var hasGoogle = _tags.comment.match(/google/i);
92022                 var commentWarning = selection.select('.form-field-comment').selectAll('.comment-warning')
92023                     .data(hasGoogle ? [0] : []);
92024
92025                 commentWarning.exit()
92026                     .transition()
92027                     .duration(200)
92028                     .style('opacity', 0)
92029                     .remove();
92030
92031                 var commentEnter = commentWarning.enter()
92032                     .insert('div', '.tag-reference-body')
92033                     .attr('class', 'field-warning comment-warning')
92034                     .style('opacity', 0);
92035
92036                 commentEnter
92037                     .append('a')
92038                     .attr('target', '_blank')
92039                     .attr('tabindex', -1)
92040                     .call(svgIcon('#iD-icon-alert', 'inline'))
92041                     .attr('href', _t('commit.google_warning_link'))
92042                     .append('span')
92043                     .text(_t('commit.google_warning'));
92044
92045                 commentEnter
92046                     .transition()
92047                     .duration(200)
92048                     .style('opacity', 1);
92049             }
92050
92051
92052             changesetEditor.tags = function(_) {
92053                 if (!arguments.length) { return _tags; }
92054                 _tags = _;
92055                 // Don't reset _fieldsArr here.
92056                 return changesetEditor;
92057             };
92058
92059
92060             changesetEditor.changesetID = function(_) {
92061                 if (!arguments.length) { return _changesetID; }
92062                 if (_changesetID === _) { return changesetEditor; }
92063                 _changesetID = _;
92064                 _fieldsArr = null;
92065                 return changesetEditor;
92066             };
92067
92068
92069             return utilRebind(changesetEditor, dispatch$1, 'on');
92070         }
92071
92072         function uiSectionChanges(context) {
92073             var detected = utilDetect();
92074
92075             var _discardTags = {};
92076             _mainFileFetcher.get('discarded')
92077                 .then(function(d) { _discardTags = d; })
92078                 .catch(function() { /* ignore */ });
92079
92080             var section = uiSection('changes-list', context)
92081                 .title(function() {
92082                     var history = context.history();
92083                     var summary = history.difference().summary();
92084                     return _t('commit.changes', { count: summary.length });
92085                 })
92086                 .disclosureContent(renderDisclosureContent);
92087
92088             function renderDisclosureContent(selection) {
92089                 var history = context.history();
92090                 var summary = history.difference().summary();
92091
92092                 var container = selection.selectAll('.commit-section')
92093                     .data([0]);
92094
92095                 var containerEnter = container.enter()
92096                     .append('div')
92097                     .attr('class', 'commit-section');
92098
92099                 containerEnter
92100                     .append('ul')
92101                     .attr('class', 'changeset-list');
92102
92103                 container = containerEnter
92104                     .merge(container);
92105
92106
92107                 var items = container.select('ul').selectAll('li')
92108                     .data(summary);
92109
92110                 var itemsEnter = items.enter()
92111                     .append('li')
92112                     .attr('class', 'change-item');
92113
92114                 itemsEnter
92115                     .each(function(d) {
92116                         select(this)
92117                             .call(svgIcon('#iD-icon-' + d.entity.geometry(d.graph), 'pre-text ' + d.changeType));
92118                     });
92119
92120                 itemsEnter
92121                     .append('span')
92122                     .attr('class', 'change-type')
92123                     .text(function(d) { return _t('commit.' + d.changeType) + ' '; });
92124
92125                 itemsEnter
92126                     .append('strong')
92127                     .attr('class', 'entity-type')
92128                     .text(function(d) {
92129                         var matched = _mainPresetIndex.match(d.entity, d.graph);
92130                         return (matched && matched.name()) || utilDisplayType(d.entity.id);
92131                     });
92132
92133                 itemsEnter
92134                     .append('span')
92135                     .attr('class', 'entity-name')
92136                     .text(function(d) {
92137                         var name = utilDisplayName(d.entity) || '',
92138                             string = '';
92139                         if (name !== '') {
92140                             string += ':';
92141                         }
92142                         return string += ' ' + name;
92143                     });
92144
92145                 itemsEnter
92146                     .style('opacity', 0)
92147                     .transition()
92148                     .style('opacity', 1);
92149
92150                 items = itemsEnter
92151                     .merge(items);
92152
92153                 items
92154                     .on('mouseover', mouseover)
92155                     .on('mouseout', mouseout)
92156                     .on('click', click);
92157
92158
92159                 // Download changeset link
92160                 var changeset = new osmChangeset().update({ id: undefined });
92161                 var changes = history.changes(actionDiscardTags(history.difference(), _discardTags));
92162
92163                 delete changeset.id;  // Export without chnageset_id
92164
92165                 var data = JXON.stringify(changeset.osmChangeJXON(changes));
92166                 var blob = new Blob([data], {type: 'text/xml;charset=utf-8;'});
92167                 var fileName = 'changes.osc';
92168
92169                 var linkEnter = container.selectAll('.download-changes')
92170                     .data([0])
92171                     .enter()
92172                     .append('a')
92173                     .attr('class', 'download-changes');
92174
92175                 if (detected.download) {      // All except IE11 and Edge
92176                     linkEnter                 // download the data as a file
92177                         .attr('href', window.URL.createObjectURL(blob))
92178                         .attr('download', fileName);
92179
92180                 } else {                      // IE11 and Edge
92181                     linkEnter                 // open data uri in a new tab
92182                         .attr('target', '_blank')
92183                         .on('click.download', function() {
92184                             navigator.msSaveBlob(blob, fileName);
92185                         });
92186                 }
92187
92188                 linkEnter
92189                     .call(svgIcon('#iD-icon-load', 'inline'))
92190                     .append('span')
92191                     .text(_t('commit.download_changes'));
92192
92193
92194                 function mouseover(d) {
92195                     if (d.entity) {
92196                         context.surface().selectAll(
92197                             utilEntityOrMemberSelector([d.entity.id], context.graph())
92198                         ).classed('hover', true);
92199                     }
92200                 }
92201
92202
92203                 function mouseout() {
92204                     context.surface().selectAll('.hover')
92205                         .classed('hover', false);
92206                 }
92207
92208
92209                 function click(change) {
92210                     if (change.changeType !== 'deleted') {
92211                         var entity = change.entity;
92212                         context.map().zoomToEase(entity);
92213                         context.surface().selectAll(utilEntityOrMemberSelector([entity.id], context.graph()))
92214                             .classed('hover', true);
92215                     }
92216                 }
92217             }
92218
92219             return section;
92220         }
92221
92222         function uiCommitWarnings(context) {
92223
92224             function commitWarnings(selection) {
92225                 var issuesBySeverity = context.validator()
92226                     .getIssuesBySeverity({ what: 'edited', where: 'all', includeDisabledRules: true });
92227
92228                 for (var severity in issuesBySeverity) {
92229                     var issues = issuesBySeverity[severity];
92230                     var section = severity + '-section';
92231                     var issueItem = severity + '-item';
92232
92233                     var container = selection.selectAll('.' + section)
92234                         .data(issues.length ? [0] : []);
92235
92236                     container.exit()
92237                         .remove();
92238
92239                     var containerEnter = container.enter()
92240                         .append('div')
92241                         .attr('class', 'modal-section ' + section + ' fillL2');
92242
92243                     containerEnter
92244                         .append('h3')
92245                         .text(severity === 'warning' ? _t('commit.warnings') : _t('commit.errors'));
92246
92247                     containerEnter
92248                         .append('ul')
92249                         .attr('class', 'changeset-list');
92250
92251                     container = containerEnter
92252                         .merge(container);
92253
92254
92255                     var items = container.select('ul').selectAll('li')
92256                         .data(issues, function(d) { return d.id; });
92257
92258                     items.exit()
92259                         .remove();
92260
92261                     var itemsEnter = items.enter()
92262                         .append('li')
92263                         .attr('class', issueItem);
92264
92265                     itemsEnter
92266                         .call(svgIcon('#iD-icon-alert', 'pre-text'));
92267
92268                     itemsEnter
92269                         .append('strong')
92270                         .attr('class', 'issue-message');
92271
92272                     itemsEnter.filter(function(d) { return d.tooltip; })
92273                         .call(uiTooltip()
92274                             .title(function(d) { return d.tooltip; })
92275                             .placement('top')
92276                         );
92277
92278                     items = itemsEnter
92279                         .merge(items);
92280
92281                     items.selectAll('.issue-message')
92282                         .text(function(d) {
92283                             return d.message(context);
92284                         });
92285
92286                     items
92287                         .on('mouseover', function(d) {
92288                             if (d.entityIds) {
92289                                 context.surface().selectAll(
92290                                     utilEntityOrMemberSelector(
92291                                         d.entityIds,
92292                                         context.graph()
92293                                     )
92294                                 ).classed('hover', true);
92295                             }
92296                         })
92297                         .on('mouseout', function() {
92298                             context.surface().selectAll('.hover')
92299                                 .classed('hover', false);
92300                         })
92301                         .on('click', function(d) {
92302                             context.validator().focusIssue(d);
92303                         });
92304                 }
92305             }
92306
92307
92308             return commitWarnings;
92309         }
92310
92311         var readOnlyTags = [
92312             /^changesets_count$/,
92313             /^created_by$/,
92314             /^ideditor:/,
92315             /^imagery_used$/,
92316             /^host$/,
92317             /^locale$/,
92318             /^warnings:/,
92319             /^resolved:/,
92320             /^closed:note$/,
92321             /^closed:keepright$/,
92322             /^closed:improveosm:/,
92323             /^closed:osmose:/
92324         ];
92325
92326         // treat most punctuation (except -, _, +, &) as hashtag delimiters - #4398
92327         // from https://stackoverflow.com/a/25575009
92328         var hashtagRegex = /(#[^\u2000-\u206F\u2E00-\u2E7F\s\\'!"#$%()*,.\/:;<=>?@\[\]^`{|}~]+)/g;
92329
92330
92331         function uiCommit(context) {
92332             var dispatch$1 = dispatch('cancel');
92333             var _userDetails;
92334             var _selection;
92335
92336             var changesetEditor = uiChangesetEditor(context)
92337                 .on('change', changeTags);
92338             var rawTagEditor = uiSectionRawTagEditor('changeset-tag-editor', context)
92339                 .on('change', changeTags)
92340                 .readOnlyTags(readOnlyTags);
92341             var commitChanges = uiSectionChanges(context);
92342             var commitWarnings = uiCommitWarnings(context);
92343
92344
92345             function commit(selection) {
92346                 _selection = selection;
92347
92348                 // Initialize changeset if one does not exist yet.
92349                 if (!context.changeset) { initChangeset(); }
92350
92351                 loadDerivedChangesetTags();
92352
92353                 selection.call(render);
92354             }
92355
92356             function initChangeset() {
92357
92358                 // expire stored comment, hashtags, source after cutoff datetime - #3947 #4899
92359                 var commentDate = +corePreferences('commentDate') || 0;
92360                 var currDate = Date.now();
92361                 var cutoff = 2 * 86400 * 1000;   // 2 days
92362                 if (commentDate > currDate || currDate - commentDate > cutoff) {
92363                     corePreferences('comment', null);
92364                     corePreferences('hashtags', null);
92365                     corePreferences('source', null);
92366                 }
92367
92368                 // load in explicitly-set values, if any
92369                 if (context.defaultChangesetComment()) {
92370                     corePreferences('comment', context.defaultChangesetComment());
92371                     corePreferences('commentDate', Date.now());
92372                 }
92373                 if (context.defaultChangesetSource()) {
92374                     corePreferences('source', context.defaultChangesetSource());
92375                     corePreferences('commentDate', Date.now());
92376                 }
92377                 if (context.defaultChangesetHashtags()) {
92378                     corePreferences('hashtags', context.defaultChangesetHashtags());
92379                     corePreferences('commentDate', Date.now());
92380                 }
92381
92382                 var detected = utilDetect();
92383                 var tags = {
92384                     comment: corePreferences('comment') || '',
92385                     created_by: context.cleanTagValue('iD ' + context.version),
92386                     host: context.cleanTagValue(detected.host),
92387                     locale: context.cleanTagValue(_mainLocalizer.localeCode())
92388                 };
92389
92390                 // call findHashtags initially - this will remove stored
92391                 // hashtags if any hashtags are found in the comment - #4304
92392                 findHashtags(tags, true);
92393
92394                 var hashtags = corePreferences('hashtags');
92395                 if (hashtags) {
92396                     tags.hashtags = hashtags;
92397                 }
92398
92399                 var source = corePreferences('source');
92400                 if (source) {
92401                     tags.source = source;
92402                 }
92403                 var photoOverlaysUsed = context.history().photoOverlaysUsed();
92404                 if (photoOverlaysUsed.length) {
92405                     var sources = (tags.source || '').split(';');
92406
92407                     // include this tag for any photo layer
92408                     if (sources.indexOf('streetlevel imagery') === -1) {
92409                         sources.push('streetlevel imagery');
92410                     }
92411
92412                     // add the photo overlays used during editing as sources
92413                     photoOverlaysUsed.forEach(function(photoOverlay) {
92414                         if (sources.indexOf(photoOverlay) === -1) {
92415                             sources.push(photoOverlay);
92416                         }
92417                     });
92418
92419                     tags.source = context.cleanTagValue(sources.join(';'));
92420                 }
92421
92422                 context.changeset = new osmChangeset({ tags: tags });
92423             }
92424
92425             // Calculates read-only metadata tags based on the user's editing session and applies
92426             // them to the changeset.
92427             function loadDerivedChangesetTags() {
92428
92429                 var osm = context.connection();
92430                 if (!osm) { return; }
92431
92432                 var tags = Object.assign({}, context.changeset.tags);   // shallow copy
92433
92434                 // assign tags for imagery used
92435                 var imageryUsed = context.cleanTagValue(context.history().imageryUsed().join(';'));
92436                 tags.imagery_used = imageryUsed || 'None';
92437
92438                 // assign tags for closed issues and notes
92439                 var osmClosed = osm.getClosedIDs();
92440                 var itemType;
92441                 if (osmClosed.length) {
92442                     tags['closed:note'] = context.cleanTagValue(osmClosed.join(';'));
92443                 }
92444                 if (services.keepRight) {
92445                     var krClosed = services.keepRight.getClosedIDs();
92446                     if (krClosed.length) {
92447                         tags['closed:keepright'] = context.cleanTagValue(krClosed.join(';'));
92448                     }
92449                 }
92450                 if (services.improveOSM) {
92451                     var iOsmClosed = services.improveOSM.getClosedCounts();
92452                     for (itemType in iOsmClosed) {
92453                         tags['closed:improveosm:' + itemType] = context.cleanTagValue(iOsmClosed[itemType].toString());
92454                     }
92455                 }
92456                 if (services.osmose) {
92457                     var osmoseClosed = services.osmose.getClosedCounts();
92458                     for (itemType in osmoseClosed) {
92459                         tags['closed:osmose:' + itemType] = context.cleanTagValue(osmoseClosed[itemType].toString());
92460                     }
92461                 }
92462
92463                 // remove existing issue counts
92464                 for (var key in tags) {
92465                     if (key.match(/(^warnings:)|(^resolved:)/)) {
92466                         delete tags[key];
92467                     }
92468                 }
92469
92470                 function addIssueCounts(issues, prefix) {
92471                     var issuesByType = utilArrayGroupBy(issues, 'type');
92472                     for (var issueType in issuesByType) {
92473                         var issuesOfType = issuesByType[issueType];
92474                         if (issuesOfType[0].subtype) {
92475                             var issuesBySubtype = utilArrayGroupBy(issuesOfType, 'subtype');
92476                             for (var issueSubtype in issuesBySubtype) {
92477                                 var issuesOfSubtype = issuesBySubtype[issueSubtype];
92478                                 tags[prefix + ':' + issueType + ':' + issueSubtype] = context.cleanTagValue(issuesOfSubtype.length.toString());
92479                             }
92480                         } else {
92481                             tags[prefix + ':' + issueType] = context.cleanTagValue(issuesOfType.length.toString());
92482                         }
92483                     }
92484                 }
92485
92486                 // add counts of warnings generated by the user's edits
92487                 var warnings = context.validator()
92488                     .getIssuesBySeverity({ what: 'edited', where: 'all', includeIgnored: true, includeDisabledRules: true }).warning;
92489                 addIssueCounts(warnings, 'warnings');
92490
92491                 // add counts of issues resolved by the user's edits
92492                 var resolvedIssues = context.validator().getResolvedIssues();
92493                 addIssueCounts(resolvedIssues, 'resolved');
92494
92495                 context.changeset = context.changeset.update({ tags: tags });
92496             }
92497
92498             function render(selection) {
92499
92500                 var osm = context.connection();
92501                 if (!osm) { return; }
92502
92503                 var header = selection.selectAll('.header')
92504                     .data([0]);
92505
92506                 var headerTitle = header.enter()
92507                     .append('div')
92508                     .attr('class', 'header fillL header-container');
92509
92510                 headerTitle
92511                     .append('div')
92512                     .attr('class', 'header-block header-block-outer');
92513
92514                 headerTitle
92515                     .append('div')
92516                     .attr('class', 'header-block')
92517                     .append('h3')
92518                     .text(_t('commit.title'));
92519
92520                 headerTitle
92521                     .append('div')
92522                     .attr('class', 'header-block header-block-outer header-block-close')
92523                     .append('button')
92524                     .attr('class', 'close')
92525                     .on('click', function() {
92526                         dispatch$1.call('cancel', this);
92527                     })
92528                     .call(svgIcon('#iD-icon-close'));
92529
92530                 var body = selection.selectAll('.body')
92531                     .data([0]);
92532
92533                 body = body.enter()
92534                     .append('div')
92535                     .attr('class', 'body')
92536                     .merge(body);
92537
92538
92539                 // Changeset Section
92540                 var changesetSection = body.selectAll('.changeset-editor')
92541                     .data([0]);
92542
92543                 changesetSection = changesetSection.enter()
92544                     .append('div')
92545                     .attr('class', 'modal-section changeset-editor')
92546                     .merge(changesetSection);
92547
92548                 changesetSection
92549                     .call(changesetEditor
92550                         .changesetID(context.changeset.id)
92551                         .tags(context.changeset.tags)
92552                     );
92553
92554
92555                 // Warnings
92556                 body.call(commitWarnings);
92557
92558
92559                 // Upload Explanation
92560                 var saveSection = body.selectAll('.save-section')
92561                     .data([0]);
92562
92563                 saveSection = saveSection.enter()
92564                     .append('div')
92565                     .attr('class','modal-section save-section fillL')
92566                     .merge(saveSection);
92567
92568                 var prose = saveSection.selectAll('.commit-info')
92569                     .data([0]);
92570
92571                 if (prose.enter().size()) {   // first time, make sure to update user details in prose
92572                     _userDetails = null;
92573                 }
92574
92575                 prose = prose.enter()
92576                     .append('p')
92577                     .attr('class', 'commit-info')
92578                     .text(_t('commit.upload_explanation'))
92579                     .merge(prose);
92580
92581                 // always check if this has changed, but only update prose.html()
92582                 // if needed, because it can trigger a style recalculation
92583                 osm.userDetails(function(err, user) {
92584                     if (err) { return; }
92585
92586                     if (_userDetails === user) { return; }  // no change
92587                     _userDetails = user;
92588
92589                     var userLink = select(document.createElement('div'));
92590
92591                     if (user.image_url) {
92592                         userLink
92593                             .append('img')
92594                             .attr('src', user.image_url)
92595                             .attr('class', 'icon pre-text user-icon');
92596                     }
92597
92598                     userLink
92599                         .append('a')
92600                         .attr('class', 'user-info')
92601                         .text(user.display_name)
92602                         .attr('href', osm.userURL(user.display_name))
92603                         .attr('target', '_blank');
92604
92605                     prose
92606                         .html(_t('commit.upload_explanation_with_user', { user: userLink.html() }));
92607                 });
92608
92609
92610                 // Request Review
92611                 var requestReview = saveSection.selectAll('.request-review')
92612                     .data([0]);
92613
92614                 // Enter
92615                 var requestReviewEnter = requestReview.enter()
92616                     .append('div')
92617                     .attr('class', 'request-review');
92618
92619                 var requestReviewDomId = utilUniqueDomId('commit-input-request-review');
92620
92621                 var labelEnter = requestReviewEnter
92622                     .append('label')
92623                     .attr('for', requestReviewDomId);
92624
92625                 labelEnter
92626                     .append('input')
92627                     .attr('type', 'checkbox')
92628                     .attr('id', requestReviewDomId);
92629
92630                 labelEnter
92631                     .append('span')
92632                     .text(_t('commit.request_review'));
92633
92634                 // Update
92635                 requestReview = requestReview
92636                     .merge(requestReviewEnter);
92637
92638                 var requestReviewInput = requestReview.selectAll('input')
92639                     .property('checked', isReviewRequested(context.changeset.tags))
92640                     .on('change', toggleRequestReview);
92641
92642
92643                 // Buttons
92644                 var buttonSection = saveSection.selectAll('.buttons')
92645                     .data([0]);
92646
92647                 // enter
92648                 var buttonEnter = buttonSection.enter()
92649                     .append('div')
92650                     .attr('class', 'buttons fillL');
92651
92652                 buttonEnter
92653                     .append('button')
92654                     .attr('class', 'secondary-action button cancel-button')
92655                     .append('span')
92656                     .attr('class', 'label')
92657                     .text(_t('commit.cancel'));
92658
92659                 var uploadButton = buttonEnter
92660                     .append('button')
92661                     .attr('class', 'action button save-button');
92662
92663                 uploadButton.append('span')
92664                     .attr('class', 'label')
92665                     .text(_t('commit.save'));
92666
92667                 var uploadBlockerTooltipText = getUploadBlockerMessage();
92668
92669                 // update
92670                 buttonSection = buttonSection
92671                     .merge(buttonEnter);
92672
92673                 buttonSection.selectAll('.cancel-button')
92674                     .on('click.cancel', function() {
92675                         dispatch$1.call('cancel', this);
92676                     });
92677
92678                 buttonSection.selectAll('.save-button')
92679                     .classed('disabled', uploadBlockerTooltipText !== null)
92680                     .on('click.save', function() {
92681                         if (!select(this).classed('disabled')) {
92682                             this.blur();    // avoid keeping focus on the button - #4641
92683                             context.uploader().save(context.changeset);
92684                         }
92685                     });
92686
92687                 // remove any existing tooltip
92688                 uiTooltip().destroyAny(buttonSection.selectAll('.save-button'));
92689
92690                 if (uploadBlockerTooltipText) {
92691                     buttonSection.selectAll('.save-button')
92692                         .call(uiTooltip().title(uploadBlockerTooltipText).placement('top'));
92693                 }
92694
92695                 // Raw Tag Editor
92696                 var tagSection = body.selectAll('.tag-section.raw-tag-editor')
92697                     .data([0]);
92698
92699                 tagSection = tagSection.enter()
92700                     .append('div')
92701                     .attr('class', 'modal-section tag-section raw-tag-editor')
92702                     .merge(tagSection);
92703
92704                 tagSection
92705                     .call(rawTagEditor
92706                         .tags(Object.assign({}, context.changeset.tags))   // shallow copy
92707                         .render
92708                     );
92709
92710                 var changesSection = body.selectAll('.commit-changes-section')
92711                     .data([0]);
92712
92713                 changesSection = changesSection.enter()
92714                     .append('div')
92715                     .attr('class', 'modal-section commit-changes-section')
92716                     .merge(changesSection);
92717
92718                 // Change summary
92719                 changesSection.call(commitChanges.render);
92720
92721
92722                 function toggleRequestReview() {
92723                     var rr = requestReviewInput.property('checked');
92724                     updateChangeset({ review_requested: (rr ? 'yes' : undefined) });
92725
92726                     tagSection
92727                         .call(rawTagEditor
92728                             .tags(Object.assign({}, context.changeset.tags))   // shallow copy
92729                             .render
92730                         );
92731                 }
92732             }
92733
92734
92735             function getUploadBlockerMessage() {
92736                 var errors = context.validator()
92737                     .getIssuesBySeverity({ what: 'edited', where: 'all' }).error;
92738
92739                 if (errors.length) {
92740                     return _t('commit.outstanding_errors_message', { count: errors.length });
92741
92742                 } else {
92743                     var hasChangesetComment = context.changeset && context.changeset.tags.comment && context.changeset.tags.comment.trim().length;
92744                     if (!hasChangesetComment) {
92745                         return _t('commit.comment_needed_message');
92746                     }
92747                 }
92748                 return null;
92749             }
92750
92751
92752             function changeTags(_, changed, onInput) {
92753                 if (changed.hasOwnProperty('comment')) {
92754                     if (changed.comment === undefined) {
92755                         changed.comment = '';
92756                     }
92757                     if (!onInput) {
92758                         corePreferences('comment', changed.comment);
92759                         corePreferences('commentDate', Date.now());
92760                     }
92761                 }
92762                 if (changed.hasOwnProperty('source')) {
92763                     if (changed.source === undefined) {
92764                         corePreferences('source', null);
92765                     } else if (!onInput) {
92766                         corePreferences('source', changed.source);
92767                         corePreferences('commentDate', Date.now());
92768                     }
92769                 }
92770                 // no need to update `prefs` for `hashtags` here since it's done in `updateChangeset`
92771
92772                 updateChangeset(changed, onInput);
92773
92774                 if (_selection) {
92775                     _selection.call(render);
92776                 }
92777             }
92778
92779
92780             function findHashtags(tags, commentOnly) {
92781                 var detectedHashtags = commentHashtags();
92782
92783                 if (detectedHashtags.length) {
92784                     // always remove stored hashtags if there are hashtags in the comment - #4304
92785                     corePreferences('hashtags', null);
92786                 }
92787                 if (!detectedHashtags.length || !commentOnly) {
92788                     detectedHashtags = detectedHashtags.concat(hashtagHashtags());
92789                 }
92790
92791                 var allLowerCase = new Set();
92792                 return detectedHashtags.filter(function(hashtag) {
92793                     // Compare tags as lowercase strings, but keep original case tags
92794                     var lowerCase = hashtag.toLowerCase();
92795                     if (!allLowerCase.has(lowerCase)) {
92796                         allLowerCase.add(lowerCase);
92797                         return true;
92798                     }
92799                     return false;
92800                 });
92801
92802                 // Extract hashtags from `comment`
92803                 function commentHashtags() {
92804                     var matches = (tags.comment || '')
92805                         .replace(/http\S*/g, '')  // drop anything that looks like a URL - #4289
92806                         .match(hashtagRegex);
92807
92808                     return matches || [];
92809                 }
92810
92811                 // Extract and clean hashtags from `hashtags`
92812                 function hashtagHashtags() {
92813                     var matches = (tags.hashtags || '')
92814                         .split(/[,;\s]+/)
92815                         .map(function (s) {
92816                             if (s[0] !== '#') { s = '#' + s; }    // prepend '#'
92817                             var matched = s.match(hashtagRegex);
92818                             return matched && matched[0];
92819                         }).filter(Boolean);                       // exclude falsy
92820
92821                     return matches || [];
92822                 }
92823             }
92824
92825
92826             function isReviewRequested(tags) {
92827                 var rr = tags.review_requested;
92828                 if (rr === undefined) { return false; }
92829                 rr = rr.trim().toLowerCase();
92830                 return !(rr === '' || rr === 'no');
92831             }
92832
92833
92834             function updateChangeset(changed, onInput) {
92835                 var tags = Object.assign({}, context.changeset.tags);   // shallow copy
92836
92837                 Object.keys(changed).forEach(function(k) {
92838                     var v = changed[k];
92839                     k = context.cleanTagKey(k);
92840                     if (readOnlyTags.indexOf(k) !== -1) { return; }
92841
92842                     if (k !== '' && v !== undefined) {
92843                         if (onInput) {
92844                             tags[k] = v;
92845                         } else {
92846                             tags[k] = context.cleanTagValue(v);
92847                         }
92848                     } else {
92849                         delete tags[k];
92850                     }
92851                 });
92852
92853                 if (!onInput) {
92854                     // when changing the comment, override hashtags with any found in comment.
92855                     var commentOnly = changed.hasOwnProperty('comment') && (changed.comment !== '');
92856                     var arr = findHashtags(tags, commentOnly);
92857                     if (arr.length) {
92858                         tags.hashtags = context.cleanTagValue(arr.join(';'));
92859                         corePreferences('hashtags', tags.hashtags);
92860                     } else {
92861                         delete tags.hashtags;
92862                         corePreferences('hashtags', null);
92863                     }
92864                 }
92865
92866                 // always update userdetails, just in case user reauthenticates as someone else
92867                 if (_userDetails && _userDetails.changesets_count !== undefined) {
92868                     var changesetsCount = parseInt(_userDetails.changesets_count, 10) + 1;  // #4283
92869                     tags.changesets_count = String(changesetsCount);
92870
92871                     // first 100 edits - new user
92872                     if (changesetsCount <= 100) {
92873                         var s;
92874                         s = corePreferences('walkthrough_completed');
92875                         if (s) {
92876                             tags['ideditor:walkthrough_completed'] = s;
92877                         }
92878
92879                         s = corePreferences('walkthrough_progress');
92880                         if (s) {
92881                             tags['ideditor:walkthrough_progress'] = s;
92882                         }
92883
92884                         s = corePreferences('walkthrough_started');
92885                         if (s) {
92886                             tags['ideditor:walkthrough_started'] = s;
92887                         }
92888                     }
92889                 } else {
92890                     delete tags.changesets_count;
92891                 }
92892
92893                 if (!fastDeepEqual(context.changeset.tags, tags)) {
92894                     context.changeset = context.changeset.update({ tags: tags });
92895                 }
92896             }
92897
92898
92899             commit.reset = function() {
92900                 context.changeset = null;
92901             };
92902
92903
92904             return utilRebind(commit, dispatch$1, 'on');
92905         }
92906
92907         var RADIUS = 6378137;
92908         var FLATTENING = 1/298.257223563;
92909         var POLAR_RADIUS$1 = 6356752.3142;
92910
92911         var wgs84 = {
92912                 RADIUS: RADIUS,
92913                 FLATTENING: FLATTENING,
92914                 POLAR_RADIUS: POLAR_RADIUS$1
92915         };
92916
92917         var geometry_1 = geometry;
92918         var ring = ringArea;
92919
92920         function geometry(_) {
92921             var area = 0, i;
92922             switch (_.type) {
92923                 case 'Polygon':
92924                     return polygonArea(_.coordinates);
92925                 case 'MultiPolygon':
92926                     for (i = 0; i < _.coordinates.length; i++) {
92927                         area += polygonArea(_.coordinates[i]);
92928                     }
92929                     return area;
92930                 case 'Point':
92931                 case 'MultiPoint':
92932                 case 'LineString':
92933                 case 'MultiLineString':
92934                     return 0;
92935                 case 'GeometryCollection':
92936                     for (i = 0; i < _.geometries.length; i++) {
92937                         area += geometry(_.geometries[i]);
92938                     }
92939                     return area;
92940             }
92941         }
92942
92943         function polygonArea(coords) {
92944             var area = 0;
92945             if (coords && coords.length > 0) {
92946                 area += Math.abs(ringArea(coords[0]));
92947                 for (var i = 1; i < coords.length; i++) {
92948                     area -= Math.abs(ringArea(coords[i]));
92949                 }
92950             }
92951             return area;
92952         }
92953
92954         /**
92955          * Calculate the approximate area of the polygon were it projected onto
92956          *     the earth.  Note that this area will be positive if ring is oriented
92957          *     clockwise, otherwise it will be negative.
92958          *
92959          * Reference:
92960          * Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for
92961          *     Polygons on a Sphere", JPL Publication 07-03, Jet Propulsion
92962          *     Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409
92963          *
92964          * Returns:
92965          * {float} The approximate signed geodesic area of the polygon in square
92966          *     meters.
92967          */
92968
92969         function ringArea(coords) {
92970             var p1, p2, p3, lowerIndex, middleIndex, upperIndex, i,
92971             area = 0,
92972             coordsLength = coords.length;
92973
92974             if (coordsLength > 2) {
92975                 for (i = 0; i < coordsLength; i++) {
92976                     if (i === coordsLength - 2) {// i = N-2
92977                         lowerIndex = coordsLength - 2;
92978                         middleIndex = coordsLength -1;
92979                         upperIndex = 0;
92980                     } else if (i === coordsLength - 1) {// i = N-1
92981                         lowerIndex = coordsLength - 1;
92982                         middleIndex = 0;
92983                         upperIndex = 1;
92984                     } else { // i = 0 to N-3
92985                         lowerIndex = i;
92986                         middleIndex = i+1;
92987                         upperIndex = i+2;
92988                     }
92989                     p1 = coords[lowerIndex];
92990                     p2 = coords[middleIndex];
92991                     p3 = coords[upperIndex];
92992                     area += ( rad(p3[0]) - rad(p1[0]) ) * Math.sin( rad(p2[1]));
92993                 }
92994
92995                 area = area * wgs84.RADIUS * wgs84.RADIUS / 2;
92996             }
92997
92998             return area;
92999         }
93000
93001         function rad(_) {
93002             return _ * Math.PI / 180;
93003         }
93004
93005         var geojsonArea = {
93006                 geometry: geometry_1,
93007                 ring: ring
93008         };
93009
93010         function toRadians(angleInDegrees) {
93011           return (angleInDegrees * Math.PI) / 180;
93012         }
93013
93014         function toDegrees(angleInRadians) {
93015           return (angleInRadians * 180) / Math.PI;
93016         }
93017
93018         function offset(c1, distance, bearing) {
93019           var lat1 = toRadians(c1[1]);
93020           var lon1 = toRadians(c1[0]);
93021           var dByR = distance / 6378137; // distance divided by 6378137 (radius of the earth) wgs84
93022           var lat = Math.asin(
93023             Math.sin(lat1) * Math.cos(dByR) +
93024               Math.cos(lat1) * Math.sin(dByR) * Math.cos(bearing)
93025           );
93026           var lon =
93027             lon1 +
93028             Math.atan2(
93029               Math.sin(bearing) * Math.sin(dByR) * Math.cos(lat1),
93030               Math.cos(dByR) - Math.sin(lat1) * Math.sin(lat)
93031             );
93032           return [toDegrees(lon), toDegrees(lat)];
93033         }
93034
93035         function validateCenter(center) {
93036           var validCenterLengths = [2, 3];
93037           if (!Array.isArray(center) || !validCenterLengths.includes(center.length)) {
93038             throw new Error("ERROR! Center has to be an array of length two or three");
93039           }
93040           var lng = center[0];
93041           var lat = center[1];
93042           if (typeof lng !== "number" || typeof lat !== "number") {
93043             throw new Error(
93044               ("ERROR! Longitude and Latitude has to be numbers but where " + (typeof lng) + " and " + (typeof lat))
93045             );
93046           }
93047           if (lng > 180 || lng < -180) {
93048             throw new Error(
93049               ("ERROR! Longitude has to be between -180 and 180 but was " + lng)
93050             );
93051           }
93052
93053           if (lat > 90 || lat < -90) {
93054             throw new Error(
93055               ("ERROR! Latitude has to be between -90 and 90 but was " + lat)
93056             );
93057           }
93058         }
93059
93060         function validateRadius(radius) {
93061           if (typeof radius !== "number") {
93062             throw new Error(
93063               ("ERROR! Radius has to be a positive number but was: " + (typeof radius))
93064             );
93065           }
93066
93067           if (radius <= 0) {
93068             throw new Error(
93069               ("ERROR! Radius has to be a positive number but was: " + radius)
93070             );
93071           }
93072         }
93073
93074         function validateNumberOfSegments(numberOfSegments) {
93075           if (typeof numberOfSegments !== "number" && numberOfSegments !== undefined) {
93076             throw new Error(
93077               ("ERROR! Number of segments has to be a number but was: " + (typeof numberOfSegments))
93078             );
93079           }
93080
93081           if (numberOfSegments < 3) {
93082             throw new Error(
93083               ("ERROR! Number of segments has to be at least 3 but was: " + numberOfSegments)
93084             );
93085           }
93086         }
93087
93088         function validateInput(ref) {
93089           var center = ref.center;
93090           var radius = ref.radius;
93091           var numberOfSegments = ref.numberOfSegments;
93092
93093           validateCenter(center);
93094           validateRadius(radius);
93095           validateNumberOfSegments(numberOfSegments);
93096         }
93097
93098         var circleToPolygon = function circleToPolygon(center, radius, numberOfSegments) {
93099           var n = numberOfSegments ? numberOfSegments : 32;
93100
93101           // validateInput() throws error on invalid input and do nothing on valid input
93102           validateInput({ center: center, radius: radius, numberOfSegments: numberOfSegments });
93103
93104           var coordinates = [];
93105           for (var i = 0; i < n; ++i) {
93106             coordinates.push(offset(center, radius, (2 * Math.PI * -i) / n));
93107           }
93108           coordinates.push(coordinates[0]);
93109
93110           return {
93111             type: "Polygon",
93112             coordinates: [coordinates]
93113           };
93114         };
93115
93116         var geojsonPrecision = createCommonjsModule(function (module) {
93117         (function() {
93118
93119           function parse(t, coordinatePrecision, extrasPrecision) {
93120
93121             function point(p) {
93122               return p.map(function(e, index) {
93123                 if (index < 2) {
93124                     return 1 * e.toFixed(coordinatePrecision);
93125                 } else {
93126                     return 1 * e.toFixed(extrasPrecision);
93127                 }
93128               });
93129             }
93130
93131             function multi(l) {
93132               return l.map(point);
93133             }
93134
93135             function poly(p) {
93136               return p.map(multi);
93137             }
93138
93139             function multiPoly(m) {
93140               return m.map(poly);
93141             }
93142
93143             function geometry(obj) {
93144               if (!obj) {
93145                 return {};
93146               }
93147               
93148               switch (obj.type) {
93149                 case "Point":
93150                   obj.coordinates = point(obj.coordinates);
93151                   return obj;
93152                 case "LineString":
93153                 case "MultiPoint":
93154                   obj.coordinates = multi(obj.coordinates);
93155                   return obj;
93156                 case "Polygon":
93157                 case "MultiLineString":
93158                   obj.coordinates = poly(obj.coordinates);
93159                   return obj;
93160                 case "MultiPolygon":
93161                   obj.coordinates = multiPoly(obj.coordinates);
93162                   return obj;
93163                 case "GeometryCollection":
93164                   obj.geometries = obj.geometries.map(geometry);
93165                   return obj;
93166                 default :
93167                   return {};
93168               }
93169             }
93170
93171             function feature(obj) {
93172               obj.geometry = geometry(obj.geometry);
93173               return obj
93174             }
93175
93176             function featureCollection(f) {
93177               f.features = f.features.map(feature);
93178               return f;
93179             }
93180
93181             function geometryCollection(g) {
93182               g.geometries = g.geometries.map(geometry);
93183               return g;
93184             }
93185
93186             if (!t) {
93187               return t;
93188             }
93189
93190             switch (t.type) {
93191               case "Feature":
93192                 return feature(t);
93193               case "GeometryCollection" :
93194                 return geometryCollection(t);
93195               case "FeatureCollection" :
93196                 return featureCollection(t);
93197               case "Point":
93198               case "LineString":
93199               case "Polygon":
93200               case "MultiPoint":
93201               case "MultiPolygon":
93202               case "MultiLineString":
93203                 return geometry(t);
93204               default :
93205                 return t;
93206             }
93207               
93208           }
93209
93210           module.exports = parse;
93211           module.exports.parse = parse;
93212
93213         }());
93214         });
93215
93216         /* Polyfill service v3.13.0
93217          * For detailed credits and licence information see http://github.com/financial-times/polyfill-service
93218          *
93219          * - Array.prototype.fill, License: CC0 */
93220
93221         if (!('fill' in Array.prototype)) {
93222           Object.defineProperty(Array.prototype, 'fill', {
93223             configurable: true,
93224             value: function fill (value) {
93225               if (this === undefined || this === null) {
93226                 throw new TypeError(this + ' is not an object')
93227               }
93228
93229               var arrayLike = Object(this);
93230
93231               var length = Math.max(Math.min(arrayLike.length, 9007199254740991), 0) || 0;
93232
93233               var relativeStart = 1 in arguments ? parseInt(Number(arguments[1]), 10) || 0 : 0;
93234
93235               relativeStart = relativeStart < 0 ? Math.max(length + relativeStart, 0) : Math.min(relativeStart, length);
93236
93237               var relativeEnd = 2 in arguments && arguments[2] !== undefined ? parseInt(Number(arguments[2]), 10) || 0 : length;
93238
93239               relativeEnd = relativeEnd < 0 ? Math.max(length + arguments[2], 0) : Math.min(relativeEnd, length);
93240
93241               while (relativeStart < relativeEnd) {
93242                 arrayLike[relativeStart] = value;
93243
93244                 ++relativeStart;
93245               }
93246
93247               return arrayLike
93248             },
93249             writable: true
93250           });
93251         }
93252
93253         /**
93254          * Polyfill for IE support
93255          */
93256         Number.isFinite = Number.isFinite || function (value) {
93257           return typeof value === 'number' && isFinite(value)
93258         };
93259
93260         Number.isInteger = Number.isInteger || function (val) {
93261           return typeof val === 'number' &&
93262           isFinite(val) &&
93263           Math.floor(val) === val
93264         };
93265
93266         Number.parseFloat = Number.parseFloat || parseFloat;
93267
93268         Number.isNaN = Number.isNaN || function (value) {
93269           return value !== value // eslint-disable-line
93270         };
93271
93272         /**
93273          * Polyfill for IE support
93274          */
93275         Math.trunc = Math.trunc || function (x) {
93276           return x < 0 ? Math.ceil(x) : Math.floor(x)
93277         };
93278
93279         var NumberUtil = function NumberUtil () {};
93280
93281         NumberUtil.prototype.interfaces_ = function interfaces_ () {
93282           return []
93283         };
93284         NumberUtil.prototype.getClass = function getClass () {
93285           return NumberUtil
93286         };
93287         NumberUtil.prototype.equalsWithTolerance = function equalsWithTolerance (x1, x2, tolerance) {
93288           return Math.abs(x1 - x2) <= tolerance
93289         };
93290
93291         var IllegalArgumentException = (function (Error) {
93292                 function IllegalArgumentException (message) {
93293                         Error.call(this, message);
93294                         this.name = 'IllegalArgumentException';
93295                         this.message = message;
93296                         this.stack = (new Error()).stack;
93297                 }
93298
93299                 if ( Error ) { IllegalArgumentException.__proto__ = Error; }
93300                 IllegalArgumentException.prototype = Object.create( Error && Error.prototype );
93301                 IllegalArgumentException.prototype.constructor = IllegalArgumentException;
93302
93303                 return IllegalArgumentException;
93304         }(Error));
93305
93306         var Double = function Double () {};
93307
93308         var staticAccessors$1 = { MAX_VALUE: { configurable: true } };
93309
93310         Double.isNaN = function isNaN (n) { return Number.isNaN(n) };
93311         Double.doubleToLongBits = function doubleToLongBits (n) { return n };
93312         Double.longBitsToDouble = function longBitsToDouble (n) { return n };
93313         Double.isInfinite = function isInfinite (n) { return !Number.isFinite(n) };
93314         staticAccessors$1.MAX_VALUE.get = function () { return Number.MAX_VALUE };
93315
93316         Object.defineProperties( Double, staticAccessors$1 );
93317
93318         var Comparable = function Comparable () {};
93319
93320         var Clonable = function Clonable () {};
93321
93322         var Comparator = function Comparator () {};
93323
93324         function Serializable () {}
93325
93326         // import Assert from '../util/Assert'
93327
93328         var Coordinate = function Coordinate () {
93329           this.x = null;
93330           this.y = null;
93331           this.z = null;
93332           if (arguments.length === 0) {
93333             this.x = 0.0;
93334             this.y = 0.0;
93335             this.z = Coordinate.NULL_ORDINATE;
93336           } else if (arguments.length === 1) {
93337             var c = arguments[0];
93338             this.x = c.x;
93339             this.y = c.y;
93340             this.z = c.z;
93341           } else if (arguments.length === 2) {
93342             this.x = arguments[0];
93343             this.y = arguments[1];
93344             this.z = Coordinate.NULL_ORDINATE;
93345           } else if (arguments.length === 3) {
93346             this.x = arguments[0];
93347             this.y = arguments[1];
93348             this.z = arguments[2];
93349           }
93350         };
93351
93352         var staticAccessors = { DimensionalComparator: { configurable: true },serialVersionUID: { configurable: true },NULL_ORDINATE: { configurable: true },X: { configurable: true },Y: { configurable: true },Z: { configurable: true } };
93353         Coordinate.prototype.setOrdinate = function setOrdinate (ordinateIndex, value) {
93354           switch (ordinateIndex) {
93355             case Coordinate.X:
93356               this.x = value;
93357               break
93358             case Coordinate.Y:
93359               this.y = value;
93360               break
93361             case Coordinate.Z:
93362               this.z = value;
93363               break
93364             default:
93365               throw new IllegalArgumentException('Invalid ordinate index: ' + ordinateIndex)
93366           }
93367         };
93368         Coordinate.prototype.equals2D = function equals2D () {
93369           if (arguments.length === 1) {
93370             var other = arguments[0];
93371             if (this.x !== other.x) {
93372               return false
93373             }
93374             if (this.y !== other.y) {
93375               return false
93376             }
93377             return true
93378           } else if (arguments.length === 2) {
93379             var c = arguments[0];
93380             var tolerance = arguments[1];
93381             if (!NumberUtil.equalsWithTolerance(this.x, c.x, tolerance)) {
93382               return false
93383             }
93384             if (!NumberUtil.equalsWithTolerance(this.y, c.y, tolerance)) {
93385               return false
93386             }
93387             return true
93388           }
93389         };
93390         Coordinate.prototype.getOrdinate = function getOrdinate (ordinateIndex) {
93391           switch (ordinateIndex) {
93392             case Coordinate.X:
93393               return this.x
93394             case Coordinate.Y:
93395               return this.y
93396             case Coordinate.Z:
93397               return this.z
93398           }
93399           throw new IllegalArgumentException('Invalid ordinate index: ' + ordinateIndex)
93400         };
93401         Coordinate.prototype.equals3D = function equals3D (other) {
93402           return this.x === other.x &&
93403                  this.y === other.y &&
93404                  ((this.z === other.z || Double.isNaN(this.z)) &&
93405                  Double.isNaN(other.z))
93406         };
93407         Coordinate.prototype.equals = function equals (other) {
93408           if (!(other instanceof Coordinate)) {
93409             return false
93410           }
93411           return this.equals2D(other)
93412         };
93413         Coordinate.prototype.equalInZ = function equalInZ (c, tolerance) {
93414           return NumberUtil.equalsWithTolerance(this.z, c.z, tolerance)
93415         };
93416         Coordinate.prototype.compareTo = function compareTo (o) {
93417           var other = o;
93418           if (this.x < other.x) { return -1 }
93419           if (this.x > other.x) { return 1 }
93420           if (this.y < other.y) { return -1 }
93421           if (this.y > other.y) { return 1 }
93422           return 0
93423         };
93424         Coordinate.prototype.clone = function clone () {
93425           // try {
93426           // var coord = null
93427           // return coord
93428           // } catch (e) {
93429           // if (e instanceof CloneNotSupportedException) {
93430           //   Assert.shouldNeverReachHere("this shouldn't happen because this class is Cloneable")
93431           //   return null
93432           // } else throw e
93433           // } finally {}
93434         };
93435         Coordinate.prototype.copy = function copy () {
93436           return new Coordinate(this)
93437         };
93438         Coordinate.prototype.toString = function toString () {
93439           return '(' + this.x + ', ' + this.y + ', ' + this.z + ')'
93440         };
93441         Coordinate.prototype.distance3D = function distance3D (c) {
93442           var dx = this.x - c.x;
93443           var dy = this.y - c.y;
93444           var dz = this.z - c.z;
93445           return Math.sqrt(dx * dx + dy * dy + dz * dz)
93446         };
93447         Coordinate.prototype.distance = function distance (c) {
93448           var dx = this.x - c.x;
93449           var dy = this.y - c.y;
93450           return Math.sqrt(dx * dx + dy * dy)
93451         };
93452         Coordinate.prototype.hashCode = function hashCode () {
93453           var result = 17;
93454           result = 37 * result + Coordinate.hashCode(this.x);
93455           result = 37 * result + Coordinate.hashCode(this.y);
93456           return result
93457         };
93458         Coordinate.prototype.setCoordinate = function setCoordinate (other) {
93459           this.x = other.x;
93460           this.y = other.y;
93461           this.z = other.z;
93462         };
93463         Coordinate.prototype.interfaces_ = function interfaces_ () {
93464           return [Comparable, Clonable, Serializable]
93465         };
93466         Coordinate.prototype.getClass = function getClass () {
93467           return Coordinate
93468         };
93469         Coordinate.hashCode = function hashCode () {
93470           if (arguments.length === 1) {
93471             var x = arguments[0];
93472             var f = Double.doubleToLongBits(x);
93473             return Math.trunc((f ^ f) >>> 32)
93474           }
93475         };
93476         staticAccessors.DimensionalComparator.get = function () { return DimensionalComparator };
93477         staticAccessors.serialVersionUID.get = function () { return 6683108902428366910 };
93478         staticAccessors.NULL_ORDINATE.get = function () { return Double.NaN };
93479         staticAccessors.X.get = function () { return 0 };
93480         staticAccessors.Y.get = function () { return 1 };
93481         staticAccessors.Z.get = function () { return 2 };
93482
93483         Object.defineProperties( Coordinate, staticAccessors );
93484
93485         var DimensionalComparator = function DimensionalComparator (dimensionsToTest) {
93486           this._dimensionsToTest = 2;
93487           if (arguments.length === 0) ; else if (arguments.length === 1) {
93488             var dimensionsToTest$1 = arguments[0];
93489             if (dimensionsToTest$1 !== 2 && dimensionsToTest$1 !== 3) { throw new IllegalArgumentException('only 2 or 3 dimensions may be specified') }
93490             this._dimensionsToTest = dimensionsToTest$1;
93491           }
93492         };
93493         DimensionalComparator.prototype.compare = function compare (o1, o2) {
93494           var c1 = o1;
93495           var c2 = o2;
93496           var compX = DimensionalComparator.compare(c1.x, c2.x);
93497           if (compX !== 0) { return compX }
93498           var compY = DimensionalComparator.compare(c1.y, c2.y);
93499           if (compY !== 0) { return compY }
93500           if (this._dimensionsToTest <= 2) { return 0 }
93501           var compZ = DimensionalComparator.compare(c1.z, c2.z);
93502           return compZ
93503         };
93504         DimensionalComparator.prototype.interfaces_ = function interfaces_ () {
93505           return [Comparator]
93506         };
93507         DimensionalComparator.prototype.getClass = function getClass () {
93508           return DimensionalComparator
93509         };
93510         DimensionalComparator.compare = function compare (a, b) {
93511           if (a < b) { return -1 }
93512           if (a > b) { return 1 }
93513           if (Double.isNaN(a)) {
93514             if (Double.isNaN(b)) { return 0 }
93515             return -1
93516           }
93517           if (Double.isNaN(b)) { return 1 }
93518           return 0
93519         };
93520
93521         // import hasInterface from '../../../../hasInterface'
93522         // import CoordinateSequence from './CoordinateSequence'
93523
93524         var CoordinateSequenceFactory = function CoordinateSequenceFactory () {};
93525
93526         CoordinateSequenceFactory.prototype.create = function create () {
93527           // if (arguments.length === 1) {
93528           // if (arguments[0] instanceof Array) {
93529           //   let coordinates = arguments[0]
93530           // } else if (hasInterface(arguments[0], CoordinateSequence)) {
93531           //   let coordSeq = arguments[0]
93532           // }
93533           // } else if (arguments.length === 2) {
93534           // let size = arguments[0]
93535           // let dimension = arguments[1]
93536           // }
93537         };
93538         CoordinateSequenceFactory.prototype.interfaces_ = function interfaces_ () {
93539           return []
93540         };
93541         CoordinateSequenceFactory.prototype.getClass = function getClass () {
93542           return CoordinateSequenceFactory
93543         };
93544
93545         var Location = function Location () {};
93546
93547         var staticAccessors$4 = { INTERIOR: { configurable: true },BOUNDARY: { configurable: true },EXTERIOR: { configurable: true },NONE: { configurable: true } };
93548
93549         Location.prototype.interfaces_ = function interfaces_ () {
93550           return []
93551         };
93552         Location.prototype.getClass = function getClass () {
93553           return Location
93554         };
93555         Location.toLocationSymbol = function toLocationSymbol (locationValue) {
93556           switch (locationValue) {
93557             case Location.EXTERIOR:
93558               return 'e'
93559             case Location.BOUNDARY:
93560               return 'b'
93561             case Location.INTERIOR:
93562               return 'i'
93563             case Location.NONE:
93564               return '-'
93565           }
93566           throw new IllegalArgumentException('Unknown location value: ' + locationValue)
93567         };
93568         staticAccessors$4.INTERIOR.get = function () { return 0 };
93569         staticAccessors$4.BOUNDARY.get = function () { return 1 };
93570         staticAccessors$4.EXTERIOR.get = function () { return 2 };
93571         staticAccessors$4.NONE.get = function () { return -1 };
93572
93573         Object.defineProperties( Location, staticAccessors$4 );
93574
93575         var hasInterface = function (o, i) {
93576           return o.interfaces_ && o.interfaces_().indexOf(i) > -1
93577         };
93578
93579         var MathUtil = function MathUtil () {};
93580
93581         var staticAccessors$5 = { LOG_10: { configurable: true } };
93582
93583         MathUtil.prototype.interfaces_ = function interfaces_ () {
93584           return []
93585         };
93586         MathUtil.prototype.getClass = function getClass () {
93587           return MathUtil
93588         };
93589         MathUtil.log10 = function log10 (x) {
93590           var ln = Math.log(x);
93591           if (Double.isInfinite(ln)) { return ln }
93592           if (Double.isNaN(ln)) { return ln }
93593           return ln / MathUtil.LOG_10
93594         };
93595         MathUtil.min = function min (v1, v2, v3, v4) {
93596           var min = v1;
93597           if (v2 < min) { min = v2; }
93598           if (v3 < min) { min = v3; }
93599           if (v4 < min) { min = v4; }
93600           return min
93601         };
93602         MathUtil.clamp = function clamp () {
93603           if (typeof arguments[2] === 'number' && (typeof arguments[0] === 'number' && typeof arguments[1] === 'number')) {
93604             var x = arguments[0];
93605             var min = arguments[1];
93606             var max = arguments[2];
93607             if (x < min) { return min }
93608             if (x > max) { return max }
93609             return x
93610           } else if (Number.isInteger(arguments[2]) && (Number.isInteger(arguments[0]) && Number.isInteger(arguments[1]))) {
93611             var x$1 = arguments[0];
93612             var min$1 = arguments[1];
93613             var max$1 = arguments[2];
93614             if (x$1 < min$1) { return min$1 }
93615             if (x$1 > max$1) { return max$1 }
93616             return x$1
93617           }
93618         };
93619         MathUtil.wrap = function wrap (index, max) {
93620           if (index < 0) {
93621             return max - -index % max
93622           }
93623           return index % max
93624         };
93625         MathUtil.max = function max () {
93626           if (arguments.length === 3) {
93627             var v1 = arguments[0];
93628             var v2 = arguments[1];
93629             var v3 = arguments[2];
93630             var max = v1;
93631             if (v2 > max) { max = v2; }
93632             if (v3 > max) { max = v3; }
93633             return max
93634           } else if (arguments.length === 4) {
93635             var v1$1 = arguments[0];
93636             var v2$1 = arguments[1];
93637             var v3$1 = arguments[2];
93638             var v4 = arguments[3];
93639             var max$1 = v1$1;
93640             if (v2$1 > max$1) { max$1 = v2$1; }
93641             if (v3$1 > max$1) { max$1 = v3$1; }
93642             if (v4 > max$1) { max$1 = v4; }
93643             return max$1
93644           }
93645         };
93646         MathUtil.average = function average (x1, x2) {
93647           return (x1 + x2) / 2.0
93648         };
93649         staticAccessors$5.LOG_10.get = function () { return Math.log(10) };
93650
93651         Object.defineProperties( MathUtil, staticAccessors$5 );
93652
93653         var StringBuffer = function StringBuffer (str) {
93654           this.str = str;
93655         };
93656         StringBuffer.prototype.append = function append (e) {
93657           this.str += e;
93658         };
93659
93660         StringBuffer.prototype.setCharAt = function setCharAt (i, c) {
93661           this.str = this.str.substr(0, i) + c + this.str.substr(i + 1);
93662         };
93663
93664         StringBuffer.prototype.toString = function toString (e) {
93665           return this.str
93666         };
93667
93668         var Integer = function Integer (value) {
93669           this.value = value;
93670         };
93671         Integer.prototype.intValue = function intValue () {
93672           return this.value
93673         };
93674         Integer.prototype.compareTo = function compareTo (o) {
93675           if (this.value < o) { return -1 }
93676           if (this.value > o) { return 1 }
93677           return 0
93678         };
93679         Integer.isNaN = function isNaN (n) { return Number.isNaN(n) };
93680
93681         var Character = function Character () {};
93682
93683         Character.isWhitespace = function isWhitespace (c) { return ((c <= 32 && c >= 0) || c === 127) };
93684         Character.toUpperCase = function toUpperCase (c) { return c.toUpperCase() };
93685
93686         var DD = function DD () {
93687           this._hi = 0.0;
93688           this._lo = 0.0;
93689           if (arguments.length === 0) {
93690             this.init(0.0);
93691           } else if (arguments.length === 1) {
93692             if (typeof arguments[0] === 'number') {
93693               var x = arguments[0];
93694               this.init(x);
93695             } else if (arguments[0] instanceof DD) {
93696               var dd = arguments[0];
93697               this.init(dd);
93698             } else if (typeof arguments[0] === 'string') {
93699               var str = arguments[0];
93700               DD.call(this, DD.parse(str));
93701             }
93702           } else if (arguments.length === 2) {
93703             var hi = arguments[0];
93704             var lo = arguments[1];
93705             this.init(hi, lo);
93706           }
93707         };
93708
93709         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 } };
93710         DD.prototype.le = function le (y) {
93711           return (this._hi < y._hi || this._hi === y._hi) && this._lo <= y._lo
93712         };
93713         DD.prototype.extractSignificantDigits = function extractSignificantDigits (insertDecimalPoint, magnitude) {
93714           var y = this.abs();
93715           var mag = DD.magnitude(y._hi);
93716           var scale = DD.TEN.pow(mag);
93717           y = y.divide(scale);
93718           if (y.gt(DD.TEN)) {
93719             y = y.divide(DD.TEN);
93720             mag += 1;
93721           } else if (y.lt(DD.ONE)) {
93722             y = y.multiply(DD.TEN);
93723             mag -= 1;
93724           }
93725           var decimalPointPos = mag + 1;
93726           var buf = new StringBuffer();
93727           var numDigits = DD.MAX_PRINT_DIGITS - 1;
93728           for (var i = 0; i <= numDigits; i++) {
93729             if (insertDecimalPoint && i === decimalPointPos) {
93730               buf.append('.');
93731             }
93732             var digit = Math.trunc(y._hi);
93733             if (digit < 0) {
93734               break
93735             }
93736             var rebiasBy10 = false;
93737             var digitChar = 0;
93738             if (digit > 9) {
93739               rebiasBy10 = true;
93740               digitChar = '9';
93741             } else {
93742               digitChar = '0' + digit;
93743             }
93744             buf.append(digitChar);
93745             y = y.subtract(DD.valueOf(digit)).multiply(DD.TEN);
93746             if (rebiasBy10) { y.selfAdd(DD.TEN); }
93747             var continueExtractingDigits = true;
93748             var remMag = DD.magnitude(y._hi);
93749             if (remMag < 0 && Math.abs(remMag) >= numDigits - i) { continueExtractingDigits = false; }
93750             if (!continueExtractingDigits) { break }
93751           }
93752           magnitude[0] = mag;
93753           return buf.toString()
93754         };
93755         DD.prototype.sqr = function sqr () {
93756           return this.multiply(this)
93757         };
93758         DD.prototype.doubleValue = function doubleValue () {
93759           return this._hi + this._lo
93760         };
93761         DD.prototype.subtract = function subtract () {
93762           if (arguments[0] instanceof DD) {
93763             var y = arguments[0];
93764             return this.add(y.negate())
93765           } else if (typeof arguments[0] === 'number') {
93766             var y$1 = arguments[0];
93767             return this.add(-y$1)
93768           }
93769         };
93770         DD.prototype.equals = function equals () {
93771           if (arguments.length === 1) {
93772             var y = arguments[0];
93773             return this._hi === y._hi && this._lo === y._lo
93774           }
93775         };
93776         DD.prototype.isZero = function isZero () {
93777           return this._hi === 0.0 && this._lo === 0.0
93778         };
93779         DD.prototype.selfSubtract = function selfSubtract () {
93780           if (arguments[0] instanceof DD) {
93781             var y = arguments[0];
93782             if (this.isNaN()) { return this }
93783             return this.selfAdd(-y._hi, -y._lo)
93784           } else if (typeof arguments[0] === 'number') {
93785             var y$1 = arguments[0];
93786             if (this.isNaN()) { return this }
93787             return this.selfAdd(-y$1, 0.0)
93788           }
93789         };
93790         DD.prototype.getSpecialNumberString = function getSpecialNumberString () {
93791           if (this.isZero()) { return '0.0' }
93792           if (this.isNaN()) { return 'NaN ' }
93793           return null
93794         };
93795         DD.prototype.min = function min (x) {
93796           if (this.le(x)) {
93797             return this
93798           } else {
93799             return x
93800           }
93801         };
93802         DD.prototype.selfDivide = function selfDivide () {
93803           if (arguments.length === 1) {
93804             if (arguments[0] instanceof DD) {
93805               var y = arguments[0];
93806               return this.selfDivide(y._hi, y._lo)
93807             } else if (typeof arguments[0] === 'number') {
93808               var y$1 = arguments[0];
93809               return this.selfDivide(y$1, 0.0)
93810             }
93811           } else if (arguments.length === 2) {
93812             var yhi = arguments[0];
93813             var ylo = arguments[1];
93814             var hc = null;
93815             var tc = null;
93816             var hy = null;
93817             var ty = null;
93818             var C = null;
93819             var c = null;
93820             var U = null;
93821             var u = null;
93822             C = this._hi / yhi;
93823             c = DD.SPLIT * C;
93824             hc = c - C;
93825             u = DD.SPLIT * yhi;
93826             hc = c - hc;
93827             tc = C - hc;
93828             hy = u - yhi;
93829             U = C * yhi;
93830             hy = u - hy;
93831             ty = yhi - hy;
93832             u = hc * hy - U + hc * ty + tc * hy + tc * ty;
93833             c = (this._hi - U - u + this._lo - C * ylo) / yhi;
93834             u = C + c;
93835             this._hi = u;
93836             this._lo = C - u + c;
93837             return this
93838           }
93839         };
93840         DD.prototype.dump = function dump () {
93841           return 'DD<' + this._hi + ', ' + this._lo + '>'
93842         };
93843         DD.prototype.divide = function divide () {
93844           if (arguments[0] instanceof DD) {
93845             var y = arguments[0];
93846             var hc = null;
93847             var tc = null;
93848             var hy = null;
93849             var ty = null;
93850             var C = null;
93851             var c = null;
93852             var U = null;
93853             var u = null;
93854             C = this._hi / y._hi;
93855             c = DD.SPLIT * C;
93856             hc = c - C;
93857             u = DD.SPLIT * y._hi;
93858             hc = c - hc;
93859             tc = C - hc;
93860             hy = u - y._hi;
93861             U = C * y._hi;
93862             hy = u - hy;
93863             ty = y._hi - hy;
93864             u = hc * hy - U + hc * ty + tc * hy + tc * ty;
93865             c = (this._hi - U - u + this._lo - C * y._lo) / y._hi;
93866             u = C + c;
93867             var zhi = u;
93868             var zlo = C - u + c;
93869             return new DD(zhi, zlo)
93870           } else if (typeof arguments[0] === 'number') {
93871             var y$1 = arguments[0];
93872             if (Double.isNaN(y$1)) { return DD.createNaN() }
93873             return DD.copy(this).selfDivide(y$1, 0.0)
93874           }
93875         };
93876         DD.prototype.ge = function ge (y) {
93877           return (this._hi > y._hi || this._hi === y._hi) && this._lo >= y._lo
93878         };
93879         DD.prototype.pow = function pow (exp) {
93880           if (exp === 0.0) { return DD.valueOf(1.0) }
93881           var r = new DD(this);
93882           var s = DD.valueOf(1.0);
93883           var n = Math.abs(exp);
93884           if (n > 1) {
93885             while (n > 0) {
93886               if (n % 2 === 1) {
93887                 s.selfMultiply(r);
93888               }
93889               n /= 2;
93890               if (n > 0) { r = r.sqr(); }
93891             }
93892           } else {
93893             s = r;
93894           }
93895           if (exp < 0) { return s.reciprocal() }
93896           return s
93897         };
93898         DD.prototype.ceil = function ceil () {
93899           if (this.isNaN()) { return DD.NaN }
93900           var fhi = Math.ceil(this._hi);
93901           var flo = 0.0;
93902           if (fhi === this._hi) {
93903             flo = Math.ceil(this._lo);
93904           }
93905           return new DD(fhi, flo)
93906         };
93907         DD.prototype.compareTo = function compareTo (o) {
93908           var other = o;
93909           if (this._hi < other._hi) { return -1 }
93910           if (this._hi > other._hi) { return 1 }
93911           if (this._lo < other._lo) { return -1 }
93912           if (this._lo > other._lo) { return 1 }
93913           return 0
93914         };
93915         DD.prototype.rint = function rint () {
93916           if (this.isNaN()) { return this }
93917           var plus5 = this.add(0.5);
93918           return plus5.floor()
93919         };
93920         DD.prototype.setValue = function setValue () {
93921           if (arguments[0] instanceof DD) {
93922             var value = arguments[0];
93923             this.init(value);
93924             return this
93925           } else if (typeof arguments[0] === 'number') {
93926             var value$1 = arguments[0];
93927             this.init(value$1);
93928             return this
93929           }
93930         };
93931         DD.prototype.max = function max (x) {
93932           if (this.ge(x)) {
93933             return this
93934           } else {
93935             return x
93936           }
93937         };
93938         DD.prototype.sqrt = function sqrt () {
93939           if (this.isZero()) { return DD.valueOf(0.0) }
93940           if (this.isNegative()) {
93941             return DD.NaN
93942           }
93943           var x = 1.0 / Math.sqrt(this._hi);
93944           var ax = this._hi * x;
93945           var axdd = DD.valueOf(ax);
93946           var diffSq = this.subtract(axdd.sqr());
93947           var d2 = diffSq._hi * (x * 0.5);
93948           return axdd.add(d2)
93949         };
93950         DD.prototype.selfAdd = function selfAdd () {
93951           if (arguments.length === 1) {
93952             if (arguments[0] instanceof DD) {
93953               var y = arguments[0];
93954               return this.selfAdd(y._hi, y._lo)
93955             } else if (typeof arguments[0] === 'number') {
93956               var y$1 = arguments[0];
93957               var H = null;
93958               var h = null;
93959               var S = null;
93960               var s = null;
93961               var e = null;
93962               var f = null;
93963               S = this._hi + y$1;
93964               e = S - this._hi;
93965               s = S - e;
93966               s = y$1 - e + (this._hi - s);
93967               f = s + this._lo;
93968               H = S + f;
93969               h = f + (S - H);
93970               this._hi = H + h;
93971               this._lo = h + (H - this._hi);
93972               return this
93973             }
93974           } else if (arguments.length === 2) {
93975             var yhi = arguments[0];
93976             var ylo = arguments[1];
93977             var H$1 = null;
93978             var h$1 = null;
93979             var T = null;
93980             var t = null;
93981             var S$1 = null;
93982             var s$1 = null;
93983             var e$1 = null;
93984             var f$1 = null;
93985             S$1 = this._hi + yhi;
93986             T = this._lo + ylo;
93987             e$1 = S$1 - this._hi;
93988             f$1 = T - this._lo;
93989             s$1 = S$1 - e$1;
93990             t = T - f$1;
93991             s$1 = yhi - e$1 + (this._hi - s$1);
93992             t = ylo - f$1 + (this._lo - t);
93993             e$1 = s$1 + T;
93994             H$1 = S$1 + e$1;
93995             h$1 = e$1 + (S$1 - H$1);
93996             e$1 = t + h$1;
93997             var zhi = H$1 + e$1;
93998             var zlo = e$1 + (H$1 - zhi);
93999             this._hi = zhi;
94000             this._lo = zlo;
94001             return this
94002           }
94003         };
94004         DD.prototype.selfMultiply = function selfMultiply () {
94005           if (arguments.length === 1) {
94006             if (arguments[0] instanceof DD) {
94007               var y = arguments[0];
94008               return this.selfMultiply(y._hi, y._lo)
94009             } else if (typeof arguments[0] === 'number') {
94010               var y$1 = arguments[0];
94011               return this.selfMultiply(y$1, 0.0)
94012             }
94013           } else if (arguments.length === 2) {
94014             var yhi = arguments[0];
94015             var ylo = arguments[1];
94016             var hx = null;
94017             var tx = null;
94018             var hy = null;
94019             var ty = null;
94020             var C = null;
94021             var c = null;
94022             C = DD.SPLIT * this._hi;
94023             hx = C - this._hi;
94024             c = DD.SPLIT * yhi;
94025             hx = C - hx;
94026             tx = this._hi - hx;
94027             hy = c - yhi;
94028             C = this._hi * yhi;
94029             hy = c - hy;
94030             ty = yhi - hy;
94031             c = hx * hy - C + hx * ty + tx * hy + tx * ty + (this._hi * ylo + this._lo * yhi);
94032             var zhi = C + c;
94033             hx = C - zhi;
94034             var zlo = c + hx;
94035             this._hi = zhi;
94036             this._lo = zlo;
94037             return this
94038           }
94039         };
94040         DD.prototype.selfSqr = function selfSqr () {
94041           return this.selfMultiply(this)
94042         };
94043         DD.prototype.floor = function floor () {
94044           if (this.isNaN()) { return DD.NaN }
94045           var fhi = Math.floor(this._hi);
94046           var flo = 0.0;
94047           if (fhi === this._hi) {
94048             flo = Math.floor(this._lo);
94049           }
94050           return new DD(fhi, flo)
94051         };
94052         DD.prototype.negate = function negate () {
94053           if (this.isNaN()) { return this }
94054           return new DD(-this._hi, -this._lo)
94055         };
94056         DD.prototype.clone = function clone () {
94057           // try {
94058           // return null
94059           // } catch (ex) {
94060           // if (ex instanceof CloneNotSupportedException) {
94061           //   return null
94062           // } else throw ex
94063           // } finally {}
94064         };
94065         DD.prototype.multiply = function multiply () {
94066           if (arguments[0] instanceof DD) {
94067             var y = arguments[0];
94068             if (y.isNaN()) { return DD.createNaN() }
94069             return DD.copy(this).selfMultiply(y)
94070           } else if (typeof arguments[0] === 'number') {
94071             var y$1 = arguments[0];
94072             if (Double.isNaN(y$1)) { return DD.createNaN() }
94073             return DD.copy(this).selfMultiply(y$1, 0.0)
94074           }
94075         };
94076         DD.prototype.isNaN = function isNaN () {
94077           return Double.isNaN(this._hi)
94078         };
94079         DD.prototype.intValue = function intValue () {
94080           return Math.trunc(this._hi)
94081         };
94082         DD.prototype.toString = function toString () {
94083           var mag = DD.magnitude(this._hi);
94084           if (mag >= -3 && mag <= 20) { return this.toStandardNotation() }
94085           return this.toSciNotation()
94086         };
94087         DD.prototype.toStandardNotation = function toStandardNotation () {
94088           var specialStr = this.getSpecialNumberString();
94089           if (specialStr !== null) { return specialStr }
94090           var magnitude = new Array(1).fill(null);
94091           var sigDigits = this.extractSignificantDigits(true, magnitude);
94092           var decimalPointPos = magnitude[0] + 1;
94093           var num = sigDigits;
94094           if (sigDigits.charAt(0) === '.') {
94095             num = '0' + sigDigits;
94096           } else if (decimalPointPos < 0) {
94097             num = '0.' + DD.stringOfChar('0', -decimalPointPos) + sigDigits;
94098           } else if (sigDigits.indexOf('.') === -1) {
94099             var numZeroes = decimalPointPos - sigDigits.length;
94100             var zeroes = DD.stringOfChar('0', numZeroes);
94101             num = sigDigits + zeroes + '.0';
94102           }
94103           if (this.isNegative()) { return '-' + num }
94104           return num
94105         };
94106         DD.prototype.reciprocal = function reciprocal () {
94107           var hc = null;
94108           var tc = null;
94109           var hy = null;
94110           var ty = null;
94111           var C = null;
94112           var c = null;
94113           var U = null;
94114           var u = null;
94115           C = 1.0 / this._hi;
94116           c = DD.SPLIT * C;
94117           hc = c - C;
94118           u = DD.SPLIT * this._hi;
94119           hc = c - hc;
94120           tc = C - hc;
94121           hy = u - this._hi;
94122           U = C * this._hi;
94123           hy = u - hy;
94124           ty = this._hi - hy;
94125           u = hc * hy - U + hc * ty + tc * hy + tc * ty;
94126           c = (1.0 - U - u - C * this._lo) / this._hi;
94127           var zhi = C + c;
94128           var zlo = C - zhi + c;
94129           return new DD(zhi, zlo)
94130         };
94131         DD.prototype.toSciNotation = function toSciNotation () {
94132           if (this.isZero()) { return DD.SCI_NOT_ZERO }
94133           var specialStr = this.getSpecialNumberString();
94134           if (specialStr !== null) { return specialStr }
94135           var magnitude = new Array(1).fill(null);
94136           var digits = this.extractSignificantDigits(false, magnitude);
94137           var expStr = DD.SCI_NOT_EXPONENT_CHAR + magnitude[0];
94138           if (digits.charAt(0) === '0') {
94139             throw new Error('Found leading zero: ' + digits)
94140           }
94141           var trailingDigits = '';
94142           if (digits.length > 1) { trailingDigits = digits.substring(1); }
94143           var digitsWithDecimal = digits.charAt(0) + '.' + trailingDigits;
94144           if (this.isNegative()) { return '-' + digitsWithDecimal + expStr }
94145           return digitsWithDecimal + expStr
94146         };
94147         DD.prototype.abs = function abs () {
94148           if (this.isNaN()) { return DD.NaN }
94149           if (this.isNegative()) { return this.negate() }
94150           return new DD(this)
94151         };
94152         DD.prototype.isPositive = function isPositive () {
94153           return (this._hi > 0.0 || this._hi === 0.0) && this._lo > 0.0
94154         };
94155         DD.prototype.lt = function lt (y) {
94156           return (this._hi < y._hi || this._hi === y._hi) && this._lo < y._lo
94157         };
94158         DD.prototype.add = function add () {
94159           if (arguments[0] instanceof DD) {
94160             var y = arguments[0];
94161             return DD.copy(this).selfAdd(y)
94162           } else if (typeof arguments[0] === 'number') {
94163             var y$1 = arguments[0];
94164             return DD.copy(this).selfAdd(y$1)
94165           }
94166         };
94167         DD.prototype.init = function init () {
94168           if (arguments.length === 1) {
94169             if (typeof arguments[0] === 'number') {
94170               var x = arguments[0];
94171               this._hi = x;
94172               this._lo = 0.0;
94173             } else if (arguments[0] instanceof DD) {
94174               var dd = arguments[0];
94175               this._hi = dd._hi;
94176               this._lo = dd._lo;
94177             }
94178           } else if (arguments.length === 2) {
94179             var hi = arguments[0];
94180             var lo = arguments[1];
94181             this._hi = hi;
94182             this._lo = lo;
94183           }
94184         };
94185         DD.prototype.gt = function gt (y) {
94186           return (this._hi > y._hi || this._hi === y._hi) && this._lo > y._lo
94187         };
94188         DD.prototype.isNegative = function isNegative () {
94189           return (this._hi < 0.0 || this._hi === 0.0) && this._lo < 0.0
94190         };
94191         DD.prototype.trunc = function trunc () {
94192           if (this.isNaN()) { return DD.NaN }
94193           if (this.isPositive()) { return this.floor(); } else { return this.ceil() }
94194         };
94195         DD.prototype.signum = function signum () {
94196           if (this._hi > 0) { return 1 }
94197           if (this._hi < 0) { return -1 }
94198           if (this._lo > 0) { return 1 }
94199           if (this._lo < 0) { return -1 }
94200           return 0
94201         };
94202         DD.prototype.interfaces_ = function interfaces_ () {
94203           return [Serializable, Comparable, Clonable]
94204         };
94205         DD.prototype.getClass = function getClass () {
94206           return DD
94207         };
94208         DD.sqr = function sqr (x) {
94209           return DD.valueOf(x).selfMultiply(x)
94210         };
94211         DD.valueOf = function valueOf () {
94212           if (typeof arguments[0] === 'string') {
94213             var str = arguments[0];
94214             return DD.parse(str)
94215           } else if (typeof arguments[0] === 'number') {
94216             var x = arguments[0];
94217             return new DD(x)
94218           }
94219         };
94220         DD.sqrt = function sqrt (x) {
94221           return DD.valueOf(x).sqrt()
94222         };
94223         DD.parse = function parse (str) {
94224           var i = 0;
94225           var strlen = str.length;
94226           while (Character.isWhitespace(str.charAt(i))) { i++; }
94227           var isNegative = false;
94228           if (i < strlen) {
94229             var signCh = str.charAt(i);
94230             if (signCh === '-' || signCh === '+') {
94231               i++;
94232               if (signCh === '-') { isNegative = true; }
94233             }
94234           }
94235           var val = new DD();
94236           var numDigits = 0;
94237           var numBeforeDec = 0;
94238           var exp = 0;
94239           while (true) {
94240             if (i >= strlen) { break }
94241             var ch = str.charAt(i);
94242             i++;
94243             if (Character.isDigit(ch)) {
94244               var d = ch - '0';
94245               val.selfMultiply(DD.TEN);
94246               val.selfAdd(d);
94247               numDigits++;
94248               continue
94249             }
94250             if (ch === '.') {
94251               numBeforeDec = numDigits;
94252               continue
94253             }
94254             if (ch === 'e' || ch === 'E') {
94255               var expStr = str.substring(i);
94256               try {
94257                 exp = Integer.parseInt(expStr);
94258               } catch (ex) {
94259                 if (ex instanceof Error) {
94260                   throw new Error('Invalid exponent ' + expStr + ' in string ' + str)
94261                 } else { throw ex }
94262               } finally {}
94263               break
94264             }
94265             throw new Error("Unexpected character '" + ch + "' at position " + i + ' in string ' + str)
94266           }
94267           var val2 = val;
94268           var numDecPlaces = numDigits - numBeforeDec - exp;
94269           if (numDecPlaces === 0) {
94270             val2 = val;
94271           } else if (numDecPlaces > 0) {
94272             var scale = DD.TEN.pow(numDecPlaces);
94273             val2 = val.divide(scale);
94274           } else if (numDecPlaces < 0) {
94275             var scale$1 = DD.TEN.pow(-numDecPlaces);
94276             val2 = val.multiply(scale$1);
94277           }
94278           if (isNegative) {
94279             return val2.negate()
94280           }
94281           return val2
94282         };
94283         DD.createNaN = function createNaN () {
94284           return new DD(Double.NaN, Double.NaN)
94285         };
94286         DD.copy = function copy (dd) {
94287           return new DD(dd)
94288         };
94289         DD.magnitude = function magnitude (x) {
94290           var xAbs = Math.abs(x);
94291           var xLog10 = Math.log(xAbs) / Math.log(10);
94292           var xMag = Math.trunc(Math.floor(xLog10));
94293           var xApprox = Math.pow(10, xMag);
94294           if (xApprox * 10 <= xAbs) { xMag += 1; }
94295           return xMag
94296         };
94297         DD.stringOfChar = function stringOfChar (ch, len) {
94298           var buf = new StringBuffer();
94299           for (var i = 0; i < len; i++) {
94300             buf.append(ch);
94301           }
94302           return buf.toString()
94303         };
94304         staticAccessors$7.PI.get = function () { return new DD(3.141592653589793116e+00, 1.224646799147353207e-16) };
94305         staticAccessors$7.TWO_PI.get = function () { return new DD(6.283185307179586232e+00, 2.449293598294706414e-16) };
94306         staticAccessors$7.PI_2.get = function () { return new DD(1.570796326794896558e+00, 6.123233995736766036e-17) };
94307         staticAccessors$7.E.get = function () { return new DD(2.718281828459045091e+00, 1.445646891729250158e-16) };
94308         staticAccessors$7.NaN.get = function () { return new DD(Double.NaN, Double.NaN) };
94309         staticAccessors$7.EPS.get = function () { return 1.23259516440783e-32 };
94310         staticAccessors$7.SPLIT.get = function () { return 134217729.0 };
94311         staticAccessors$7.MAX_PRINT_DIGITS.get = function () { return 32 };
94312         staticAccessors$7.TEN.get = function () { return DD.valueOf(10.0) };
94313         staticAccessors$7.ONE.get = function () { return DD.valueOf(1.0) };
94314         staticAccessors$7.SCI_NOT_EXPONENT_CHAR.get = function () { return 'E' };
94315         staticAccessors$7.SCI_NOT_ZERO.get = function () { return '0.0E0' };
94316
94317         Object.defineProperties( DD, staticAccessors$7 );
94318
94319         var CGAlgorithmsDD = function CGAlgorithmsDD () {};
94320
94321         var staticAccessors$6 = { DP_SAFE_EPSILON: { configurable: true } };
94322
94323         CGAlgorithmsDD.prototype.interfaces_ = function interfaces_ () {
94324           return []
94325         };
94326         CGAlgorithmsDD.prototype.getClass = function getClass () {
94327           return CGAlgorithmsDD
94328         };
94329         CGAlgorithmsDD.orientationIndex = function orientationIndex (p1, p2, q) {
94330           var index = CGAlgorithmsDD.orientationIndexFilter(p1, p2, q);
94331           if (index <= 1) { return index }
94332           var dx1 = DD.valueOf(p2.x).selfAdd(-p1.x);
94333           var dy1 = DD.valueOf(p2.y).selfAdd(-p1.y);
94334           var dx2 = DD.valueOf(q.x).selfAdd(-p2.x);
94335           var dy2 = DD.valueOf(q.y).selfAdd(-p2.y);
94336           return dx1.selfMultiply(dy2).selfSubtract(dy1.selfMultiply(dx2)).signum()
94337         };
94338         CGAlgorithmsDD.signOfDet2x2 = function signOfDet2x2 (x1, y1, x2, y2) {
94339           var det = x1.multiply(y2).selfSubtract(y1.multiply(x2));
94340           return det.signum()
94341         };
94342         CGAlgorithmsDD.intersection = function intersection (p1, p2, q1, q2) {
94343           var denom1 = DD.valueOf(q2.y).selfSubtract(q1.y).selfMultiply(DD.valueOf(p2.x).selfSubtract(p1.x));
94344           var denom2 = DD.valueOf(q2.x).selfSubtract(q1.x).selfMultiply(DD.valueOf(p2.y).selfSubtract(p1.y));
94345           var denom = denom1.subtract(denom2);
94346           var numx1 = DD.valueOf(q2.x).selfSubtract(q1.x).selfMultiply(DD.valueOf(p1.y).selfSubtract(q1.y));
94347           var numx2 = DD.valueOf(q2.y).selfSubtract(q1.y).selfMultiply(DD.valueOf(p1.x).selfSubtract(q1.x));
94348           var numx = numx1.subtract(numx2);
94349           var fracP = numx.selfDivide(denom).doubleValue();
94350           var x = DD.valueOf(p1.x).selfAdd(DD.valueOf(p2.x).selfSubtract(p1.x).selfMultiply(fracP)).doubleValue();
94351           var numy1 = DD.valueOf(p2.x).selfSubtract(p1.x).selfMultiply(DD.valueOf(p1.y).selfSubtract(q1.y));
94352           var numy2 = DD.valueOf(p2.y).selfSubtract(p1.y).selfMultiply(DD.valueOf(p1.x).selfSubtract(q1.x));
94353           var numy = numy1.subtract(numy2);
94354           var fracQ = numy.selfDivide(denom).doubleValue();
94355           var y = DD.valueOf(q1.y).selfAdd(DD.valueOf(q2.y).selfSubtract(q1.y).selfMultiply(fracQ)).doubleValue();
94356           return new Coordinate(x, y)
94357         };
94358         CGAlgorithmsDD.orientationIndexFilter = function orientationIndexFilter (pa, pb, pc) {
94359           var detsum = null;
94360           var detleft = (pa.x - pc.x) * (pb.y - pc.y);
94361           var detright = (pa.y - pc.y) * (pb.x - pc.x);
94362           var det = detleft - detright;
94363           if (detleft > 0.0) {
94364             if (detright <= 0.0) {
94365               return CGAlgorithmsDD.signum(det)
94366             } else {
94367               detsum = detleft + detright;
94368             }
94369           } else if (detleft < 0.0) {
94370             if (detright >= 0.0) {
94371               return CGAlgorithmsDD.signum(det)
94372             } else {
94373               detsum = -detleft - detright;
94374             }
94375           } else {
94376             return CGAlgorithmsDD.signum(det)
94377           }
94378           var errbound = CGAlgorithmsDD.DP_SAFE_EPSILON * detsum;
94379           if (det >= errbound || -det >= errbound) {
94380             return CGAlgorithmsDD.signum(det)
94381           }
94382           return 2
94383         };
94384         CGAlgorithmsDD.signum = function signum (x) {
94385           if (x > 0) { return 1 }
94386           if (x < 0) { return -1 }
94387           return 0
94388         };
94389         staticAccessors$6.DP_SAFE_EPSILON.get = function () { return 1e-15 };
94390
94391         Object.defineProperties( CGAlgorithmsDD, staticAccessors$6 );
94392
94393         var CoordinateSequence = function CoordinateSequence () {};
94394
94395         var staticAccessors$8 = { X: { configurable: true },Y: { configurable: true },Z: { configurable: true },M: { configurable: true } };
94396
94397         staticAccessors$8.X.get = function () { return 0 };
94398         staticAccessors$8.Y.get = function () { return 1 };
94399         staticAccessors$8.Z.get = function () { return 2 };
94400         staticAccessors$8.M.get = function () { return 3 };
94401         CoordinateSequence.prototype.setOrdinate = function setOrdinate (index, ordinateIndex, value) {};
94402         CoordinateSequence.prototype.size = function size () {};
94403         CoordinateSequence.prototype.getOrdinate = function getOrdinate (index, ordinateIndex) {};
94404         CoordinateSequence.prototype.getCoordinate = function getCoordinate () {};
94405         CoordinateSequence.prototype.getCoordinateCopy = function getCoordinateCopy (i) {};
94406         CoordinateSequence.prototype.getDimension = function getDimension () {};
94407         CoordinateSequence.prototype.getX = function getX (index) {};
94408         CoordinateSequence.prototype.clone = function clone () {};
94409         CoordinateSequence.prototype.expandEnvelope = function expandEnvelope (env) {};
94410         CoordinateSequence.prototype.copy = function copy () {};
94411         CoordinateSequence.prototype.getY = function getY (index) {};
94412         CoordinateSequence.prototype.toCoordinateArray = function toCoordinateArray () {};
94413         CoordinateSequence.prototype.interfaces_ = function interfaces_ () {
94414           return [Clonable]
94415         };
94416         CoordinateSequence.prototype.getClass = function getClass () {
94417           return CoordinateSequence
94418         };
94419
94420         Object.defineProperties( CoordinateSequence, staticAccessors$8 );
94421
94422         var Exception = function Exception () {};
94423
94424         var NotRepresentableException = (function (Exception$$1) {
94425           function NotRepresentableException () {
94426             Exception$$1.call(this, 'Projective point not representable on the Cartesian plane.');
94427           }
94428
94429           if ( Exception$$1 ) { NotRepresentableException.__proto__ = Exception$$1; }
94430           NotRepresentableException.prototype = Object.create( Exception$$1 && Exception$$1.prototype );
94431           NotRepresentableException.prototype.constructor = NotRepresentableException;
94432           NotRepresentableException.prototype.interfaces_ = function interfaces_ () {
94433             return []
94434           };
94435           NotRepresentableException.prototype.getClass = function getClass () {
94436             return NotRepresentableException
94437           };
94438
94439           return NotRepresentableException;
94440         }(Exception));
94441
94442         var System = function System () {};
94443
94444         System.arraycopy = function arraycopy (src, srcPos, dest, destPos, len) {
94445           var c = 0;
94446           for (var i = srcPos; i < srcPos + len; i++) {
94447             dest[destPos + c] = src[i];
94448             c++;
94449           }
94450         };
94451
94452         System.getProperty = function getProperty (name) {
94453           return {
94454             'line.separator': '\n'
94455           }[name]
94456         };
94457
94458         var HCoordinate = function HCoordinate () {
94459           this.x = null;
94460           this.y = null;
94461           this.w = null;
94462           if (arguments.length === 0) {
94463             this.x = 0.0;
94464             this.y = 0.0;
94465             this.w = 1.0;
94466           } else if (arguments.length === 1) {
94467             var p = arguments[0];
94468             this.x = p.x;
94469             this.y = p.y;
94470             this.w = 1.0;
94471           } else if (arguments.length === 2) {
94472             if (typeof arguments[0] === 'number' && typeof arguments[1] === 'number') {
94473               var _x = arguments[0];
94474               var _y = arguments[1];
94475               this.x = _x;
94476               this.y = _y;
94477               this.w = 1.0;
94478             } else if (arguments[0] instanceof HCoordinate && arguments[1] instanceof HCoordinate) {
94479               var p1 = arguments[0];
94480               var p2 = arguments[1];
94481               this.x = p1.y * p2.w - p2.y * p1.w;
94482               this.y = p2.x * p1.w - p1.x * p2.w;
94483               this.w = p1.x * p2.y - p2.x * p1.y;
94484             } else if (arguments[0] instanceof Coordinate && arguments[1] instanceof Coordinate) {
94485               var p1$1 = arguments[0];
94486               var p2$1 = arguments[1];
94487               this.x = p1$1.y - p2$1.y;
94488               this.y = p2$1.x - p1$1.x;
94489               this.w = p1$1.x * p2$1.y - p2$1.x * p1$1.y;
94490             }
94491           } else if (arguments.length === 3) {
94492             var _x$1 = arguments[0];
94493             var _y$1 = arguments[1];
94494             var _w = arguments[2];
94495             this.x = _x$1;
94496             this.y = _y$1;
94497             this.w = _w;
94498           } else if (arguments.length === 4) {
94499             var p1$2 = arguments[0];
94500             var p2$2 = arguments[1];
94501             var q1 = arguments[2];
94502             var q2 = arguments[3];
94503             var px = p1$2.y - p2$2.y;
94504             var py = p2$2.x - p1$2.x;
94505             var pw = p1$2.x * p2$2.y - p2$2.x * p1$2.y;
94506             var qx = q1.y - q2.y;
94507             var qy = q2.x - q1.x;
94508             var qw = q1.x * q2.y - q2.x * q1.y;
94509             this.x = py * qw - qy * pw;
94510             this.y = qx * pw - px * qw;
94511             this.w = px * qy - qx * py;
94512           }
94513         };
94514         HCoordinate.prototype.getY = function getY () {
94515           var a = this.y / this.w;
94516           if (Double.isNaN(a) || Double.isInfinite(a)) {
94517             throw new NotRepresentableException()
94518           }
94519           return a
94520         };
94521         HCoordinate.prototype.getX = function getX () {
94522           var a = this.x / this.w;
94523           if (Double.isNaN(a) || Double.isInfinite(a)) {
94524             throw new NotRepresentableException()
94525           }
94526           return a
94527         };
94528         HCoordinate.prototype.getCoordinate = function getCoordinate () {
94529           var p = new Coordinate();
94530           p.x = this.getX();
94531           p.y = this.getY();
94532           return p
94533         };
94534         HCoordinate.prototype.interfaces_ = function interfaces_ () {
94535           return []
94536         };
94537         HCoordinate.prototype.getClass = function getClass () {
94538           return HCoordinate
94539         };
94540         HCoordinate.intersection = function intersection (p1, p2, q1, q2) {
94541           var px = p1.y - p2.y;
94542           var py = p2.x - p1.x;
94543           var pw = p1.x * p2.y - p2.x * p1.y;
94544           var qx = q1.y - q2.y;
94545           var qy = q2.x - q1.x;
94546           var qw = q1.x * q2.y - q2.x * q1.y;
94547           var x = py * qw - qy * pw;
94548           var y = qx * pw - px * qw;
94549           var w = px * qy - qx * py;
94550           var xInt = x / w;
94551           var yInt = y / w;
94552           if (Double.isNaN(xInt) || (Double.isInfinite(xInt) || Double.isNaN(yInt)) || Double.isInfinite(yInt)) {
94553             throw new NotRepresentableException()
94554           }
94555           return new Coordinate(xInt, yInt)
94556         };
94557
94558         var Envelope = function Envelope () {
94559           this._minx = null;
94560           this._maxx = null;
94561           this._miny = null;
94562           this._maxy = null;
94563           if (arguments.length === 0) {
94564             this.init();
94565           } else if (arguments.length === 1) {
94566             if (arguments[0] instanceof Coordinate) {
94567               var p = arguments[0];
94568               this.init(p.x, p.x, p.y, p.y);
94569             } else if (arguments[0] instanceof Envelope) {
94570               var env = arguments[0];
94571               this.init(env);
94572             }
94573           } else if (arguments.length === 2) {
94574             var p1 = arguments[0];
94575             var p2 = arguments[1];
94576             this.init(p1.x, p2.x, p1.y, p2.y);
94577           } else if (arguments.length === 4) {
94578             var x1 = arguments[0];
94579             var x2 = arguments[1];
94580             var y1 = arguments[2];
94581             var y2 = arguments[3];
94582             this.init(x1, x2, y1, y2);
94583           }
94584         };
94585
94586         var staticAccessors$9 = { serialVersionUID: { configurable: true } };
94587         Envelope.prototype.getArea = function getArea () {
94588           return this.getWidth() * this.getHeight()
94589         };
94590         Envelope.prototype.equals = function equals (other) {
94591           if (!(other instanceof Envelope)) {
94592             return false
94593           }
94594           var otherEnvelope = other;
94595           if (this.isNull()) {
94596             return otherEnvelope.isNull()
94597           }
94598           return this._maxx === otherEnvelope.getMaxX() && this._maxy === otherEnvelope.getMaxY() && this._minx === otherEnvelope.getMinX() && this._miny === otherEnvelope.getMinY()
94599         };
94600         Envelope.prototype.intersection = function intersection (env) {
94601           if (this.isNull() || env.isNull() || !this.intersects(env)) { return new Envelope() }
94602           var intMinX = this._minx > env._minx ? this._minx : env._minx;
94603           var intMinY = this._miny > env._miny ? this._miny : env._miny;
94604           var intMaxX = this._maxx < env._maxx ? this._maxx : env._maxx;
94605           var intMaxY = this._maxy < env._maxy ? this._maxy : env._maxy;
94606           return new Envelope(intMinX, intMaxX, intMinY, intMaxY)
94607         };
94608         Envelope.prototype.isNull = function isNull () {
94609           return this._maxx < this._minx
94610         };
94611         Envelope.prototype.getMaxX = function getMaxX () {
94612           return this._maxx
94613         };
94614         Envelope.prototype.covers = function covers () {
94615           if (arguments.length === 1) {
94616             if (arguments[0] instanceof Coordinate) {
94617               var p = arguments[0];
94618               return this.covers(p.x, p.y)
94619             } else if (arguments[0] instanceof Envelope) {
94620               var other = arguments[0];
94621               if (this.isNull() || other.isNull()) {
94622                 return false
94623               }
94624               return other.getMinX() >= this._minx && other.getMaxX() <= this._maxx && other.getMinY() >= this._miny && other.getMaxY() <= this._maxy
94625             }
94626           } else if (arguments.length === 2) {
94627             var x = arguments[0];
94628             var y = arguments[1];
94629             if (this.isNull()) { return false }
94630             return x >= this._minx && x <= this._maxx && y >= this._miny && y <= this._maxy
94631           }
94632         };
94633         Envelope.prototype.intersects = function intersects () {
94634           if (arguments.length === 1) {
94635             if (arguments[0] instanceof Envelope) {
94636               var other = arguments[0];
94637               if (this.isNull() || other.isNull()) {
94638                 return false
94639               }
94640               return !(other._minx > this._maxx || other._maxx < this._minx || other._miny > this._maxy || other._maxy < this._miny)
94641             } else if (arguments[0] instanceof Coordinate) {
94642               var p = arguments[0];
94643               return this.intersects(p.x, p.y)
94644             }
94645           } else if (arguments.length === 2) {
94646             var x = arguments[0];
94647             var y = arguments[1];
94648             if (this.isNull()) { return false }
94649             return !(x > this._maxx || x < this._minx || y > this._maxy || y < this._miny)
94650           }
94651         };
94652         Envelope.prototype.getMinY = function getMinY () {
94653           return this._miny
94654         };
94655         Envelope.prototype.getMinX = function getMinX () {
94656           return this._minx
94657         };
94658         Envelope.prototype.expandToInclude = function expandToInclude () {
94659           if (arguments.length === 1) {
94660             if (arguments[0] instanceof Coordinate) {
94661               var p = arguments[0];
94662               this.expandToInclude(p.x, p.y);
94663             } else if (arguments[0] instanceof Envelope) {
94664               var other = arguments[0];
94665               if (other.isNull()) {
94666                 return null
94667               }
94668               if (this.isNull()) {
94669                 this._minx = other.getMinX();
94670                 this._maxx = other.getMaxX();
94671                 this._miny = other.getMinY();
94672                 this._maxy = other.getMaxY();
94673               } else {
94674                 if (other._minx < this._minx) {
94675                   this._minx = other._minx;
94676                 }
94677                 if (other._maxx > this._maxx) {
94678                   this._maxx = other._maxx;
94679                 }
94680                 if (other._miny < this._miny) {
94681                   this._miny = other._miny;
94682                 }
94683                 if (other._maxy > this._maxy) {
94684                   this._maxy = other._maxy;
94685                 }
94686               }
94687             }
94688           } else if (arguments.length === 2) {
94689             var x = arguments[0];
94690             var y = arguments[1];
94691             if (this.isNull()) {
94692               this._minx = x;
94693               this._maxx = x;
94694               this._miny = y;
94695               this._maxy = y;
94696             } else {
94697               if (x < this._minx) {
94698                 this._minx = x;
94699               }
94700               if (x > this._maxx) {
94701                 this._maxx = x;
94702               }
94703               if (y < this._miny) {
94704                 this._miny = y;
94705               }
94706               if (y > this._maxy) {
94707                 this._maxy = y;
94708               }
94709             }
94710           }
94711         };
94712         Envelope.prototype.minExtent = function minExtent () {
94713           if (this.isNull()) { return 0.0 }
94714           var w = this.getWidth();
94715           var h = this.getHeight();
94716           if (w < h) { return w }
94717           return h
94718         };
94719         Envelope.prototype.getWidth = function getWidth () {
94720           if (this.isNull()) {
94721             return 0
94722           }
94723           return this._maxx - this._minx
94724         };
94725         Envelope.prototype.compareTo = function compareTo (o) {
94726           var env = o;
94727           if (this.isNull()) {
94728             if (env.isNull()) { return 0 }
94729             return -1
94730           } else {
94731             if (env.isNull()) { return 1 }
94732           }
94733           if (this._minx < env._minx) { return -1 }
94734           if (this._minx > env._minx) { return 1 }
94735           if (this._miny < env._miny) { return -1 }
94736           if (this._miny > env._miny) { return 1 }
94737           if (this._maxx < env._maxx) { return -1 }
94738           if (this._maxx > env._maxx) { return 1 }
94739           if (this._maxy < env._maxy) { return -1 }
94740           if (this._maxy > env._maxy) { return 1 }
94741           return 0
94742         };
94743         Envelope.prototype.translate = function translate (transX, transY) {
94744           if (this.isNull()) {
94745             return null
94746           }
94747           this.init(this.getMinX() + transX, this.getMaxX() + transX, this.getMinY() + transY, this.getMaxY() + transY);
94748         };
94749         Envelope.prototype.toString = function toString () {
94750           return 'Env[' + this._minx + ' : ' + this._maxx + ', ' + this._miny + ' : ' + this._maxy + ']'
94751         };
94752         Envelope.prototype.setToNull = function setToNull () {
94753           this._minx = 0;
94754           this._maxx = -1;
94755           this._miny = 0;
94756           this._maxy = -1;
94757         };
94758         Envelope.prototype.getHeight = function getHeight () {
94759           if (this.isNull()) {
94760             return 0
94761           }
94762           return this._maxy - this._miny
94763         };
94764         Envelope.prototype.maxExtent = function maxExtent () {
94765           if (this.isNull()) { return 0.0 }
94766           var w = this.getWidth();
94767           var h = this.getHeight();
94768           if (w > h) { return w }
94769           return h
94770         };
94771         Envelope.prototype.expandBy = function expandBy () {
94772           if (arguments.length === 1) {
94773             var distance = arguments[0];
94774             this.expandBy(distance, distance);
94775           } else if (arguments.length === 2) {
94776             var deltaX = arguments[0];
94777             var deltaY = arguments[1];
94778             if (this.isNull()) { return null }
94779             this._minx -= deltaX;
94780             this._maxx += deltaX;
94781             this._miny -= deltaY;
94782             this._maxy += deltaY;
94783             if (this._minx > this._maxx || this._miny > this._maxy) { this.setToNull(); }
94784           }
94785         };
94786         Envelope.prototype.contains = function contains () {
94787           if (arguments.length === 1) {
94788             if (arguments[0] instanceof Envelope) {
94789               var other = arguments[0];
94790               return this.covers(other)
94791             } else if (arguments[0] instanceof Coordinate) {
94792               var p = arguments[0];
94793               return this.covers(p)
94794             }
94795           } else if (arguments.length === 2) {
94796             var x = arguments[0];
94797             var y = arguments[1];
94798             return this.covers(x, y)
94799           }
94800         };
94801         Envelope.prototype.centre = function centre () {
94802           if (this.isNull()) { return null }
94803           return new Coordinate((this.getMinX() + this.getMaxX()) / 2.0, (this.getMinY() + this.getMaxY()) / 2.0)
94804         };
94805         Envelope.prototype.init = function init () {
94806           if (arguments.length === 0) {
94807             this.setToNull();
94808           } else if (arguments.length === 1) {
94809             if (arguments[0] instanceof Coordinate) {
94810               var p = arguments[0];
94811               this.init(p.x, p.x, p.y, p.y);
94812             } else if (arguments[0] instanceof Envelope) {
94813               var env = arguments[0];
94814               this._minx = env._minx;
94815               this._maxx = env._maxx;
94816               this._miny = env._miny;
94817               this._maxy = env._maxy;
94818             }
94819           } else if (arguments.length === 2) {
94820             var p1 = arguments[0];
94821             var p2 = arguments[1];
94822             this.init(p1.x, p2.x, p1.y, p2.y);
94823           } else if (arguments.length === 4) {
94824             var x1 = arguments[0];
94825             var x2 = arguments[1];
94826             var y1 = arguments[2];
94827             var y2 = arguments[3];
94828             if (x1 < x2) {
94829               this._minx = x1;
94830               this._maxx = x2;
94831             } else {
94832               this._minx = x2;
94833               this._maxx = x1;
94834             }
94835             if (y1 < y2) {
94836               this._miny = y1;
94837               this._maxy = y2;
94838             } else {
94839               this._miny = y2;
94840               this._maxy = y1;
94841             }
94842           }
94843         };
94844         Envelope.prototype.getMaxY = function getMaxY () {
94845           return this._maxy
94846         };
94847         Envelope.prototype.distance = function distance (env) {
94848           if (this.intersects(env)) { return 0 }
94849           var dx = 0.0;
94850           if (this._maxx < env._minx) { dx = env._minx - this._maxx; } else if (this._minx > env._maxx) { dx = this._minx - env._maxx; }
94851           var dy = 0.0;
94852           if (this._maxy < env._miny) { dy = env._miny - this._maxy; } else if (this._miny > env._maxy) { dy = this._miny - env._maxy; }
94853           if (dx === 0.0) { return dy }
94854           if (dy === 0.0) { return dx }
94855           return Math.sqrt(dx * dx + dy * dy)
94856         };
94857         Envelope.prototype.hashCode = function hashCode () {
94858           var result = 17;
94859           result = 37 * result + Coordinate.hashCode(this._minx);
94860           result = 37 * result + Coordinate.hashCode(this._maxx);
94861           result = 37 * result + Coordinate.hashCode(this._miny);
94862           result = 37 * result + Coordinate.hashCode(this._maxy);
94863           return result
94864         };
94865         Envelope.prototype.interfaces_ = function interfaces_ () {
94866           return [Comparable, Serializable]
94867         };
94868         Envelope.prototype.getClass = function getClass () {
94869           return Envelope
94870         };
94871         Envelope.intersects = function intersects () {
94872           if (arguments.length === 3) {
94873             var p1 = arguments[0];
94874             var p2 = arguments[1];
94875             var q = arguments[2];
94876             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))) {
94877               return true
94878             }
94879             return false
94880           } else if (arguments.length === 4) {
94881             var p1$1 = arguments[0];
94882             var p2$1 = arguments[1];
94883             var q1 = arguments[2];
94884             var q2 = arguments[3];
94885             var minq = Math.min(q1.x, q2.x);
94886             var maxq = Math.max(q1.x, q2.x);
94887             var minp = Math.min(p1$1.x, p2$1.x);
94888             var maxp = Math.max(p1$1.x, p2$1.x);
94889             if (minp > maxq) { return false }
94890             if (maxp < minq) { return false }
94891             minq = Math.min(q1.y, q2.y);
94892             maxq = Math.max(q1.y, q2.y);
94893             minp = Math.min(p1$1.y, p2$1.y);
94894             maxp = Math.max(p1$1.y, p2$1.y);
94895             if (minp > maxq) { return false }
94896             if (maxp < minq) { return false }
94897             return true
94898           }
94899         };
94900         staticAccessors$9.serialVersionUID.get = function () { return 5873921885273102420 };
94901
94902         Object.defineProperties( Envelope, staticAccessors$9 );
94903
94904         var regExes = {
94905           'typeStr': /^\s*(\w+)\s*\(\s*(.*)\s*\)\s*$/,
94906           'emptyTypeStr': /^\s*(\w+)\s*EMPTY\s*$/,
94907           'spaces': /\s+/,
94908           'parenComma': /\)\s*,\s*\(/,
94909           'doubleParenComma': /\)\s*\)\s*,\s*\(\s*\(/, // can't use {2} here
94910           'trimParens': /^\s*\(?(.*?)\)?\s*$/
94911         };
94912
94913         /**
94914          * Class for reading and writing Well-Known Text.
94915          *
94916          * NOTE: Adapted from OpenLayers 2.11 implementation.
94917          */
94918
94919         /** Create a new parser for WKT
94920          *
94921          * @param {GeometryFactory} geometryFactory
94922          * @return An instance of WKTParser.
94923          * @constructor
94924          * @private
94925          */
94926         var WKTParser = function WKTParser (geometryFactory) {
94927           this.geometryFactory = geometryFactory || new GeometryFactory();
94928         };
94929         /**
94930          * Deserialize a WKT string and return a geometry. Supports WKT for POINT,
94931          * MULTIPOINT, LINESTRING, LINEARRING, MULTILINESTRING, POLYGON, MULTIPOLYGON,
94932          * and GEOMETRYCOLLECTION.
94933          *
94934          * @param {String} wkt A WKT string.
94935          * @return {Geometry} A geometry instance.
94936          * @private
94937          */
94938         WKTParser.prototype.read = function read (wkt) {
94939           var geometry, type, str;
94940           wkt = wkt.replace(/[\n\r]/g, ' ');
94941           var matches = regExes.typeStr.exec(wkt);
94942           if (wkt.search('EMPTY') !== -1) {
94943             matches = regExes.emptyTypeStr.exec(wkt);
94944             matches[2] = undefined;
94945           }
94946           if (matches) {
94947             type = matches[1].toLowerCase();
94948             str = matches[2];
94949             if (parse$1[type]) {
94950               geometry = parse$1[type].apply(this, [str]);
94951             }
94952           }
94953
94954           if (geometry === undefined) { throw new Error('Could not parse WKT ' + wkt) }
94955
94956           return geometry
94957         };
94958
94959         /**
94960          * Serialize a geometry into a WKT string.
94961          *
94962          * @param {Geometry} geometry A feature or array of features.
94963          * @return {String} The WKT string representation of the input geometries.
94964          * @private
94965          */
94966         WKTParser.prototype.write = function write (geometry) {
94967           return this.extractGeometry(geometry)
94968         };
94969
94970         /**
94971          * Entry point to construct the WKT for a single Geometry object.
94972          *
94973          * @param {Geometry} geometry
94974          * @return {String} A WKT string of representing the geometry.
94975          * @private
94976          */
94977         WKTParser.prototype.extractGeometry = function extractGeometry (geometry) {
94978           var type = geometry.getGeometryType().toLowerCase();
94979           if (!extract$1[type]) {
94980             return null
94981           }
94982           var wktType = type.toUpperCase();
94983           var data;
94984           if (geometry.isEmpty()) {
94985             data = wktType + ' EMPTY';
94986           } else {
94987             data = wktType + '(' + extract$1[type].apply(this, [geometry]) + ')';
94988           }
94989           return data
94990         };
94991
94992         /**
94993          * Object with properties corresponding to the geometry types. Property values
94994          * are functions that do the actual data extraction.
94995          * @private
94996          */
94997         var extract$1 = {
94998           coordinate: function coordinate (coordinate$1) {
94999             return coordinate$1.x + ' ' + coordinate$1.y
95000           },
95001
95002           /**
95003            * Return a space delimited string of point coordinates.
95004            *
95005            * @param {Point}
95006            *          point
95007            * @return {String} A string of coordinates representing the point.
95008            */
95009           point: function point (point$1) {
95010             return extract$1.coordinate.call(this, point$1._coordinates._coordinates[0])
95011           },
95012
95013           /**
95014            * Return a comma delimited string of point coordinates from a multipoint.
95015            *
95016            * @param {MultiPoint}
95017            *          multipoint
95018            * @return {String} A string of point coordinate strings representing the
95019            *         multipoint.
95020            */
95021           multipoint: function multipoint (multipoint$1) {
95022             var this$1 = this;
95023
95024             var array = [];
95025             for (var i = 0, len = multipoint$1._geometries.length; i < len; ++i) {
95026               array.push('(' + extract$1.point.apply(this$1, [multipoint$1._geometries[i]]) + ')');
95027             }
95028             return array.join(',')
95029           },
95030
95031           /**
95032            * Return a comma delimited string of point coordinates from a line.
95033            *
95034            * @param {LineString} linestring
95035            * @return {String} A string of point coordinate strings representing the linestring.
95036            */
95037           linestring: function linestring (linestring$1) {
95038             var this$1 = this;
95039
95040             var array = [];
95041             for (var i = 0, len = linestring$1._points._coordinates.length; i < len; ++i) {
95042               array.push(extract$1.coordinate.apply(this$1, [linestring$1._points._coordinates[i]]));
95043             }
95044             return array.join(',')
95045           },
95046
95047           linearring: function linearring (linearring$1) {
95048             var this$1 = this;
95049
95050             var array = [];
95051             for (var i = 0, len = linearring$1._points._coordinates.length; i < len; ++i) {
95052               array.push(extract$1.coordinate.apply(this$1, [linearring$1._points._coordinates[i]]));
95053             }
95054             return array.join(',')
95055           },
95056
95057           /**
95058            * Return a comma delimited string of linestring strings from a
95059            * multilinestring.
95060            *
95061            * @param {MultiLineString} multilinestring
95062            * @return {String} A string of of linestring strings representing the multilinestring.
95063            */
95064           multilinestring: function multilinestring (multilinestring$1) {
95065             var this$1 = this;
95066
95067             var array = [];
95068             for (var i = 0, len = multilinestring$1._geometries.length; i < len; ++i) {
95069               array.push('(' +
95070                 extract$1.linestring.apply(this$1, [multilinestring$1._geometries[i]]) +
95071                 ')');
95072             }
95073             return array.join(',')
95074           },
95075
95076           /**
95077            * Return a comma delimited string of linear ring arrays from a polygon.
95078            *
95079            * @param {Polygon} polygon
95080            * @return {String} An array of linear ring arrays representing the polygon.
95081            */
95082           polygon: function polygon (polygon$1) {
95083             var this$1 = this;
95084
95085             var array = [];
95086             array.push('(' + extract$1.linestring.apply(this, [polygon$1._shell]) + ')');
95087             for (var i = 0, len = polygon$1._holes.length; i < len; ++i) {
95088               array.push('(' + extract$1.linestring.apply(this$1, [polygon$1._holes[i]]) + ')');
95089             }
95090             return array.join(',')
95091           },
95092
95093           /**
95094            * Return an array of polygon arrays from a multipolygon.
95095            *
95096            * @param {MultiPolygon} multipolygon
95097            * @return {String} An array of polygon arrays representing the multipolygon.
95098            */
95099           multipolygon: function multipolygon (multipolygon$1) {
95100             var this$1 = this;
95101
95102             var array = [];
95103             for (var i = 0, len = multipolygon$1._geometries.length; i < len; ++i) {
95104               array.push('(' + extract$1.polygon.apply(this$1, [multipolygon$1._geometries[i]]) + ')');
95105             }
95106             return array.join(',')
95107           },
95108
95109           /**
95110            * Return the WKT portion between 'GEOMETRYCOLLECTION(' and ')' for an
95111            * geometrycollection.
95112            *
95113            * @param {GeometryCollection} collection
95114            * @return {String} internal WKT representation of the collection.
95115            */
95116           geometrycollection: function geometrycollection (collection) {
95117             var this$1 = this;
95118
95119             var array = [];
95120             for (var i = 0, len = collection._geometries.length; i < len; ++i) {
95121               array.push(this$1.extractGeometry(collection._geometries[i]));
95122             }
95123             return array.join(',')
95124           }
95125         };
95126
95127         /**
95128          * Object with properties corresponding to the geometry types. Property values
95129          * are functions that do the actual parsing.
95130          * @private
95131          */
95132         var parse$1 = {
95133           /**
95134            * Return point geometry given a point WKT fragment.
95135            *
95136            * @param {String} str A WKT fragment representing the point.
95137            * @return {Point} A point geometry.
95138            * @private
95139            */
95140           point: function point (str) {
95141             if (str === undefined) {
95142               return this.geometryFactory.createPoint()
95143             }
95144
95145             var coords = str.trim().split(regExes.spaces);
95146             return this.geometryFactory.createPoint(new Coordinate(Number.parseFloat(coords[0]),
95147               Number.parseFloat(coords[1])))
95148           },
95149
95150           /**
95151            * Return a multipoint geometry given a multipoint WKT fragment.
95152            *
95153            * @param {String} str A WKT fragment representing the multipoint.
95154            * @return {Point} A multipoint feature.
95155            * @private
95156            */
95157           multipoint: function multipoint (str) {
95158             var this$1 = this;
95159
95160             if (str === undefined) {
95161               return this.geometryFactory.createMultiPoint()
95162             }
95163
95164             var point;
95165             var points = str.trim().split(',');
95166             var components = [];
95167             for (var i = 0, len = points.length; i < len; ++i) {
95168               point = points[i].replace(regExes.trimParens, '$1');
95169               components.push(parse$1.point.apply(this$1, [point]));
95170             }
95171             return this.geometryFactory.createMultiPoint(components)
95172           },
95173
95174           /**
95175            * Return a linestring geometry given a linestring WKT fragment.
95176            *
95177            * @param {String} str A WKT fragment representing the linestring.
95178            * @return {LineString} A linestring geometry.
95179            * @private
95180            */
95181           linestring: function linestring (str) {
95182             if (str === undefined) {
95183               return this.geometryFactory.createLineString()
95184             }
95185
95186             var points = str.trim().split(',');
95187             var components = [];
95188             var coords;
95189             for (var i = 0, len = points.length; i < len; ++i) {
95190               coords = points[i].trim().split(regExes.spaces);
95191               components.push(new Coordinate(Number.parseFloat(coords[0]), Number.parseFloat(coords[1])));
95192             }
95193             return this.geometryFactory.createLineString(components)
95194           },
95195
95196           /**
95197            * Return a linearring geometry given a linearring WKT fragment.
95198            *
95199            * @param {String} str A WKT fragment representing the linearring.
95200            * @return {LinearRing} A linearring geometry.
95201            * @private
95202            */
95203           linearring: function linearring (str) {
95204             if (str === undefined) {
95205               return this.geometryFactory.createLinearRing()
95206             }
95207
95208             var points = str.trim().split(',');
95209             var components = [];
95210             var coords;
95211             for (var i = 0, len = points.length; i < len; ++i) {
95212               coords = points[i].trim().split(regExes.spaces);
95213               components.push(new Coordinate(Number.parseFloat(coords[0]), Number.parseFloat(coords[1])));
95214             }
95215             return this.geometryFactory.createLinearRing(components)
95216           },
95217
95218           /**
95219            * Return a multilinestring geometry given a multilinestring WKT fragment.
95220            *
95221            * @param {String} str A WKT fragment representing the multilinestring.
95222            * @return {MultiLineString} A multilinestring geometry.
95223            * @private
95224            */
95225           multilinestring: function multilinestring (str) {
95226             var this$1 = this;
95227
95228             if (str === undefined) {
95229               return this.geometryFactory.createMultiLineString()
95230             }
95231
95232             var line;
95233             var lines = str.trim().split(regExes.parenComma);
95234             var components = [];
95235             for (var i = 0, len = lines.length; i < len; ++i) {
95236               line = lines[i].replace(regExes.trimParens, '$1');
95237               components.push(parse$1.linestring.apply(this$1, [line]));
95238             }
95239             return this.geometryFactory.createMultiLineString(components)
95240           },
95241
95242           /**
95243            * Return a polygon geometry given a polygon WKT fragment.
95244            *
95245            * @param {String} str A WKT fragment representing the polygon.
95246            * @return {Polygon} A polygon geometry.
95247            * @private
95248            */
95249           polygon: function polygon (str) {
95250             var this$1 = this;
95251
95252             if (str === undefined) {
95253               return this.geometryFactory.createPolygon()
95254             }
95255
95256             var ring, linestring, linearring;
95257             var rings = str.trim().split(regExes.parenComma);
95258             var shell;
95259             var holes = [];
95260             for (var i = 0, len = rings.length; i < len; ++i) {
95261               ring = rings[i].replace(regExes.trimParens, '$1');
95262               linestring = parse$1.linestring.apply(this$1, [ring]);
95263               linearring = this$1.geometryFactory.createLinearRing(linestring._points);
95264               if (i === 0) {
95265                 shell = linearring;
95266               } else {
95267                 holes.push(linearring);
95268               }
95269             }
95270             return this.geometryFactory.createPolygon(shell, holes)
95271           },
95272
95273           /**
95274            * Return a multipolygon geometry given a multipolygon WKT fragment.
95275            *
95276            * @param {String} str A WKT fragment representing the multipolygon.
95277            * @return {MultiPolygon} A multipolygon geometry.
95278            * @private
95279            */
95280           multipolygon: function multipolygon (str) {
95281             var this$1 = this;
95282
95283             if (str === undefined) {
95284               return this.geometryFactory.createMultiPolygon()
95285             }
95286
95287             var polygon;
95288             var polygons = str.trim().split(regExes.doubleParenComma);
95289             var components = [];
95290             for (var i = 0, len = polygons.length; i < len; ++i) {
95291               polygon = polygons[i].replace(regExes.trimParens, '$1');
95292               components.push(parse$1.polygon.apply(this$1, [polygon]));
95293             }
95294             return this.geometryFactory.createMultiPolygon(components)
95295           },
95296
95297           /**
95298            * Return a geometrycollection given a geometrycollection WKT fragment.
95299            *
95300            * @param {String} str A WKT fragment representing the geometrycollection.
95301            * @return {GeometryCollection}
95302            * @private
95303            */
95304           geometrycollection: function geometrycollection (str) {
95305             var this$1 = this;
95306
95307             if (str === undefined) {
95308               return this.geometryFactory.createGeometryCollection()
95309             }
95310
95311             // separate components of the collection with |
95312             str = str.replace(/,\s*([A-Za-z])/g, '|$1');
95313             var wktArray = str.trim().split('|');
95314             var components = [];
95315             for (var i = 0, len = wktArray.length; i < len; ++i) {
95316               components.push(this$1.read(wktArray[i]));
95317             }
95318             return this.geometryFactory.createGeometryCollection(components)
95319           }
95320         };
95321
95322         /**
95323          * Writes the Well-Known Text representation of a {@link Geometry}. The
95324          * Well-Known Text format is defined in the <A
95325          * HREF="http://www.opengis.org/techno/specs.htm"> OGC Simple Features
95326          * Specification for SQL</A>.
95327          * <p>
95328          * The <code>WKTWriter</code> outputs coordinates rounded to the precision
95329          * model. Only the maximum number of decimal places necessary to represent the
95330          * ordinates to the required precision will be output.
95331          * <p>
95332          * The SFS WKT spec does not define a special tag for {@link LinearRing}s.
95333          * Under the spec, rings are output as <code>LINESTRING</code>s.
95334          */
95335
95336         /**
95337          * @param {GeometryFactory} geometryFactory
95338          * @constructor
95339          */
95340         var WKTWriter = function WKTWriter (geometryFactory) {
95341           this.parser = new WKTParser(geometryFactory);
95342         };
95343
95344         /**
95345          * Converts a <code>Geometry</code> to its Well-known Text representation.
95346          *
95347          * @param {Geometry} geometry a <code>Geometry</code> to process.
95348          * @return {string} a <Geometry Tagged Text> string (see the OpenGIS Simple
95349          *       Features Specification).
95350          * @memberof WKTWriter
95351          */
95352         WKTWriter.prototype.write = function write (geometry) {
95353           return this.parser.write(geometry)
95354         };
95355         /**
95356          * Generates the WKT for a <tt>LINESTRING</tt> specified by two
95357          * {@link Coordinate}s.
95358          *
95359          * @param p0 the first coordinate.
95360          * @param p1 the second coordinate.
95361          *
95362          * @return the WKT.
95363          * @private
95364          */
95365         WKTWriter.toLineString = function toLineString (p0, p1) {
95366           if (arguments.length !== 2) {
95367             throw new Error('Not implemented')
95368           }
95369           return 'LINESTRING ( ' + p0.x + ' ' + p0.y + ', ' + p1.x + ' ' + p1.y + ' )'
95370         };
95371
95372         var RuntimeException = (function (Error) {
95373           function RuntimeException (message) {
95374             Error.call(this, message);
95375             this.name = 'RuntimeException';
95376             this.message = message;
95377             this.stack = (new Error()).stack;
95378           }
95379
95380           if ( Error ) { RuntimeException.__proto__ = Error; }
95381           RuntimeException.prototype = Object.create( Error && Error.prototype );
95382           RuntimeException.prototype.constructor = RuntimeException;
95383
95384           return RuntimeException;
95385         }(Error));
95386
95387         var AssertionFailedException = (function (RuntimeException$$1) {
95388           function AssertionFailedException () {
95389             RuntimeException$$1.call(this);
95390             if (arguments.length === 0) {
95391               RuntimeException$$1.call(this);
95392             } else if (arguments.length === 1) {
95393               var message = arguments[0];
95394               RuntimeException$$1.call(this, message);
95395             }
95396           }
95397
95398           if ( RuntimeException$$1 ) { AssertionFailedException.__proto__ = RuntimeException$$1; }
95399           AssertionFailedException.prototype = Object.create( RuntimeException$$1 && RuntimeException$$1.prototype );
95400           AssertionFailedException.prototype.constructor = AssertionFailedException;
95401           AssertionFailedException.prototype.interfaces_ = function interfaces_ () {
95402             return []
95403           };
95404           AssertionFailedException.prototype.getClass = function getClass () {
95405             return AssertionFailedException
95406           };
95407
95408           return AssertionFailedException;
95409         }(RuntimeException));
95410
95411         var Assert = function Assert () {};
95412
95413         Assert.prototype.interfaces_ = function interfaces_ () {
95414           return []
95415         };
95416         Assert.prototype.getClass = function getClass () {
95417           return Assert
95418         };
95419         Assert.shouldNeverReachHere = function shouldNeverReachHere () {
95420           if (arguments.length === 0) {
95421             Assert.shouldNeverReachHere(null);
95422           } else if (arguments.length === 1) {
95423             var message = arguments[0];
95424             throw new AssertionFailedException('Should never reach here' + (message !== null ? ': ' + message : ''))
95425           }
95426         };
95427         Assert.isTrue = function isTrue () {
95428           var assertion;
95429           var message;
95430           if (arguments.length === 1) {
95431             assertion = arguments[0];
95432             Assert.isTrue(assertion, null);
95433           } else if (arguments.length === 2) {
95434             assertion = arguments[0];
95435             message = arguments[1];
95436             if (!assertion) {
95437               if (message === null) {
95438                 throw new AssertionFailedException()
95439               } else {
95440                 throw new AssertionFailedException(message)
95441               }
95442             }
95443           }
95444         };
95445         Assert.equals = function equals () {
95446           var expectedValue;
95447           var actualValue;
95448           var message;
95449           if (arguments.length === 2) {
95450             expectedValue = arguments[0];
95451             actualValue = arguments[1];
95452             Assert.equals(expectedValue, actualValue, null);
95453           } else if (arguments.length === 3) {
95454             expectedValue = arguments[0];
95455             actualValue = arguments[1];
95456             message = arguments[2];
95457             if (!actualValue.equals(expectedValue)) {
95458               throw new AssertionFailedException('Expected ' + expectedValue + ' but encountered ' + actualValue + (message !== null ? ': ' + message : ''))
95459             }
95460           }
95461         };
95462
95463         var LineIntersector = function LineIntersector () {
95464           this._result = null;
95465           this._inputLines = Array(2).fill().map(function () { return Array(2); });
95466           this._intPt = new Array(2).fill(null);
95467           this._intLineIndex = null;
95468           this._isProper = null;
95469           this._pa = null;
95470           this._pb = null;
95471           this._precisionModel = null;
95472           this._intPt[0] = new Coordinate();
95473           this._intPt[1] = new Coordinate();
95474           this._pa = this._intPt[0];
95475           this._pb = this._intPt[1];
95476           this._result = 0;
95477         };
95478
95479         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 } };
95480         LineIntersector.prototype.getIndexAlongSegment = function getIndexAlongSegment (segmentIndex, intIndex) {
95481           this.computeIntLineIndex();
95482           return this._intLineIndex[segmentIndex][intIndex]
95483         };
95484         LineIntersector.prototype.getTopologySummary = function getTopologySummary () {
95485           var catBuf = new StringBuffer();
95486           if (this.isEndPoint()) { catBuf.append(' endpoint'); }
95487           if (this._isProper) { catBuf.append(' proper'); }
95488           if (this.isCollinear()) { catBuf.append(' collinear'); }
95489           return catBuf.toString()
95490         };
95491         LineIntersector.prototype.computeIntersection = function computeIntersection (p1, p2, p3, p4) {
95492           this._inputLines[0][0] = p1;
95493           this._inputLines[0][1] = p2;
95494           this._inputLines[1][0] = p3;
95495           this._inputLines[1][1] = p4;
95496           this._result = this.computeIntersect(p1, p2, p3, p4);
95497         };
95498         LineIntersector.prototype.getIntersectionNum = function getIntersectionNum () {
95499           return this._result
95500         };
95501         LineIntersector.prototype.computeIntLineIndex = function computeIntLineIndex () {
95502           if (arguments.length === 0) {
95503             if (this._intLineIndex === null) {
95504               this._intLineIndex = Array(2).fill().map(function () { return Array(2); });
95505               this.computeIntLineIndex(0);
95506               this.computeIntLineIndex(1);
95507             }
95508           } else if (arguments.length === 1) {
95509             var segmentIndex = arguments[0];
95510             var dist0 = this.getEdgeDistance(segmentIndex, 0);
95511             var dist1 = this.getEdgeDistance(segmentIndex, 1);
95512             if (dist0 > dist1) {
95513               this._intLineIndex[segmentIndex][0] = 0;
95514               this._intLineIndex[segmentIndex][1] = 1;
95515             } else {
95516               this._intLineIndex[segmentIndex][0] = 1;
95517               this._intLineIndex[segmentIndex][1] = 0;
95518             }
95519           }
95520         };
95521         LineIntersector.prototype.isProper = function isProper () {
95522           return this.hasIntersection() && this._isProper
95523         };
95524         LineIntersector.prototype.setPrecisionModel = function setPrecisionModel (precisionModel) {
95525           this._precisionModel = precisionModel;
95526         };
95527         LineIntersector.prototype.isInteriorIntersection = function isInteriorIntersection () {
95528             var this$1 = this;
95529
95530           if (arguments.length === 0) {
95531             if (this.isInteriorIntersection(0)) { return true }
95532             if (this.isInteriorIntersection(1)) { return true }
95533             return false
95534           } else if (arguments.length === 1) {
95535             var inputLineIndex = arguments[0];
95536             for (var i = 0; i < this._result; i++) {
95537               if (!(this$1._intPt[i].equals2D(this$1._inputLines[inputLineIndex][0]) || this$1._intPt[i].equals2D(this$1._inputLines[inputLineIndex][1]))) {
95538                 return true
95539               }
95540             }
95541             return false
95542           }
95543         };
95544         LineIntersector.prototype.getIntersection = function getIntersection (intIndex) {
95545           return this._intPt[intIndex]
95546         };
95547         LineIntersector.prototype.isEndPoint = function isEndPoint () {
95548           return this.hasIntersection() && !this._isProper
95549         };
95550         LineIntersector.prototype.hasIntersection = function hasIntersection () {
95551           return this._result !== LineIntersector.NO_INTERSECTION
95552         };
95553         LineIntersector.prototype.getEdgeDistance = function getEdgeDistance (segmentIndex, intIndex) {
95554           var dist = LineIntersector.computeEdgeDistance(this._intPt[intIndex], this._inputLines[segmentIndex][0], this._inputLines[segmentIndex][1]);
95555           return dist
95556         };
95557         LineIntersector.prototype.isCollinear = function isCollinear () {
95558           return this._result === LineIntersector.COLLINEAR_INTERSECTION
95559         };
95560         LineIntersector.prototype.toString = function toString () {
95561           return WKTWriter.toLineString(this._inputLines[0][0], this._inputLines[0][1]) + ' - ' + WKTWriter.toLineString(this._inputLines[1][0], this._inputLines[1][1]) + this.getTopologySummary()
95562         };
95563         LineIntersector.prototype.getEndpoint = function getEndpoint (segmentIndex, ptIndex) {
95564           return this._inputLines[segmentIndex][ptIndex]
95565         };
95566         LineIntersector.prototype.isIntersection = function isIntersection (pt) {
95567             var this$1 = this;
95568
95569           for (var i = 0; i < this._result; i++) {
95570             if (this$1._intPt[i].equals2D(pt)) {
95571               return true
95572             }
95573           }
95574           return false
95575         };
95576         LineIntersector.prototype.getIntersectionAlongSegment = function getIntersectionAlongSegment (segmentIndex, intIndex) {
95577           this.computeIntLineIndex();
95578           return this._intPt[this._intLineIndex[segmentIndex][intIndex]]
95579         };
95580         LineIntersector.prototype.interfaces_ = function interfaces_ () {
95581           return []
95582         };
95583         LineIntersector.prototype.getClass = function getClass () {
95584           return LineIntersector
95585         };
95586         LineIntersector.computeEdgeDistance = function computeEdgeDistance (p, p0, p1) {
95587           var dx = Math.abs(p1.x - p0.x);
95588           var dy = Math.abs(p1.y - p0.y);
95589           var dist = -1.0;
95590           if (p.equals(p0)) {
95591             dist = 0.0;
95592           } else if (p.equals(p1)) {
95593             if (dx > dy) { dist = dx; } else { dist = dy; }
95594           } else {
95595             var pdx = Math.abs(p.x - p0.x);
95596             var pdy = Math.abs(p.y - p0.y);
95597             if (dx > dy) { dist = pdx; } else { dist = pdy; }
95598             if (dist === 0.0 && !p.equals(p0)) {
95599               dist = Math.max(pdx, pdy);
95600             }
95601           }
95602           Assert.isTrue(!(dist === 0.0 && !p.equals(p0)), 'Bad distance calculation');
95603           return dist
95604         };
95605         LineIntersector.nonRobustComputeEdgeDistance = function nonRobustComputeEdgeDistance (p, p1, p2) {
95606           var dx = p.x - p1.x;
95607           var dy = p.y - p1.y;
95608           var dist = Math.sqrt(dx * dx + dy * dy);
95609           Assert.isTrue(!(dist === 0.0 && !p.equals(p1)), 'Invalid distance calculation');
95610           return dist
95611         };
95612         staticAccessors$10.DONT_INTERSECT.get = function () { return 0 };
95613         staticAccessors$10.DO_INTERSECT.get = function () { return 1 };
95614         staticAccessors$10.COLLINEAR.get = function () { return 2 };
95615         staticAccessors$10.NO_INTERSECTION.get = function () { return 0 };
95616         staticAccessors$10.POINT_INTERSECTION.get = function () { return 1 };
95617         staticAccessors$10.COLLINEAR_INTERSECTION.get = function () { return 2 };
95618
95619         Object.defineProperties( LineIntersector, staticAccessors$10 );
95620
95621         var RobustLineIntersector = (function (LineIntersector$$1) {
95622           function RobustLineIntersector () {
95623             LineIntersector$$1.apply(this, arguments);
95624           }
95625
95626           if ( LineIntersector$$1 ) { RobustLineIntersector.__proto__ = LineIntersector$$1; }
95627           RobustLineIntersector.prototype = Object.create( LineIntersector$$1 && LineIntersector$$1.prototype );
95628           RobustLineIntersector.prototype.constructor = RobustLineIntersector;
95629
95630           RobustLineIntersector.prototype.isInSegmentEnvelopes = function isInSegmentEnvelopes (intPt) {
95631             var env0 = new Envelope(this._inputLines[0][0], this._inputLines[0][1]);
95632             var env1 = new Envelope(this._inputLines[1][0], this._inputLines[1][1]);
95633             return env0.contains(intPt) && env1.contains(intPt)
95634           };
95635           RobustLineIntersector.prototype.computeIntersection = function computeIntersection () {
95636             if (arguments.length === 3) {
95637               var p = arguments[0];
95638               var p1 = arguments[1];
95639               var p2 = arguments[2];
95640               this._isProper = false;
95641               if (Envelope.intersects(p1, p2, p)) {
95642                 if (CGAlgorithms.orientationIndex(p1, p2, p) === 0 && CGAlgorithms.orientationIndex(p2, p1, p) === 0) {
95643                   this._isProper = true;
95644                   if (p.equals(p1) || p.equals(p2)) {
95645                     this._isProper = false;
95646                   }
95647                   this._result = LineIntersector$$1.POINT_INTERSECTION;
95648                   return null
95649                 }
95650               }
95651               this._result = LineIntersector$$1.NO_INTERSECTION;
95652             } else { return LineIntersector$$1.prototype.computeIntersection.apply(this, arguments) }
95653           };
95654           RobustLineIntersector.prototype.normalizeToMinimum = function normalizeToMinimum (n1, n2, n3, n4, normPt) {
95655             normPt.x = this.smallestInAbsValue(n1.x, n2.x, n3.x, n4.x);
95656             normPt.y = this.smallestInAbsValue(n1.y, n2.y, n3.y, n4.y);
95657             n1.x -= normPt.x;
95658             n1.y -= normPt.y;
95659             n2.x -= normPt.x;
95660             n2.y -= normPt.y;
95661             n3.x -= normPt.x;
95662             n3.y -= normPt.y;
95663             n4.x -= normPt.x;
95664             n4.y -= normPt.y;
95665           };
95666           RobustLineIntersector.prototype.safeHCoordinateIntersection = function safeHCoordinateIntersection (p1, p2, q1, q2) {
95667             var intPt = null;
95668             try {
95669               intPt = HCoordinate.intersection(p1, p2, q1, q2);
95670             } catch (e) {
95671               if (e instanceof NotRepresentableException) {
95672                 intPt = RobustLineIntersector.nearestEndpoint(p1, p2, q1, q2);
95673               } else { throw e }
95674             } finally {}
95675             return intPt
95676           };
95677           RobustLineIntersector.prototype.intersection = function intersection (p1, p2, q1, q2) {
95678             var intPt = this.intersectionWithNormalization(p1, p2, q1, q2);
95679             if (!this.isInSegmentEnvelopes(intPt)) {
95680               intPt = new Coordinate(RobustLineIntersector.nearestEndpoint(p1, p2, q1, q2));
95681             }
95682             if (this._precisionModel !== null) {
95683               this._precisionModel.makePrecise(intPt);
95684             }
95685             return intPt
95686           };
95687           RobustLineIntersector.prototype.smallestInAbsValue = function smallestInAbsValue (x1, x2, x3, x4) {
95688             var x = x1;
95689             var xabs = Math.abs(x);
95690             if (Math.abs(x2) < xabs) {
95691               x = x2;
95692               xabs = Math.abs(x2);
95693             }
95694             if (Math.abs(x3) < xabs) {
95695               x = x3;
95696               xabs = Math.abs(x3);
95697             }
95698             if (Math.abs(x4) < xabs) {
95699               x = x4;
95700             }
95701             return x
95702           };
95703           RobustLineIntersector.prototype.checkDD = function checkDD (p1, p2, q1, q2, intPt) {
95704             var intPtDD = CGAlgorithmsDD.intersection(p1, p2, q1, q2);
95705             var isIn = this.isInSegmentEnvelopes(intPtDD);
95706             System.out.println('DD in env = ' + isIn + '  --------------------- ' + intPtDD);
95707             if (intPt.distance(intPtDD) > 0.0001) {
95708               System.out.println('Distance = ' + intPt.distance(intPtDD));
95709             }
95710           };
95711           RobustLineIntersector.prototype.intersectionWithNormalization = function intersectionWithNormalization (p1, p2, q1, q2) {
95712             var n1 = new Coordinate(p1);
95713             var n2 = new Coordinate(p2);
95714             var n3 = new Coordinate(q1);
95715             var n4 = new Coordinate(q2);
95716             var normPt = new Coordinate();
95717             this.normalizeToEnvCentre(n1, n2, n3, n4, normPt);
95718             var intPt = this.safeHCoordinateIntersection(n1, n2, n3, n4);
95719             intPt.x += normPt.x;
95720             intPt.y += normPt.y;
95721             return intPt
95722           };
95723           RobustLineIntersector.prototype.computeCollinearIntersection = function computeCollinearIntersection (p1, p2, q1, q2) {
95724             var p1q1p2 = Envelope.intersects(p1, p2, q1);
95725             var p1q2p2 = Envelope.intersects(p1, p2, q2);
95726             var q1p1q2 = Envelope.intersects(q1, q2, p1);
95727             var q1p2q2 = Envelope.intersects(q1, q2, p2);
95728             if (p1q1p2 && p1q2p2) {
95729               this._intPt[0] = q1;
95730               this._intPt[1] = q2;
95731               return LineIntersector$$1.COLLINEAR_INTERSECTION
95732             }
95733             if (q1p1q2 && q1p2q2) {
95734               this._intPt[0] = p1;
95735               this._intPt[1] = p2;
95736               return LineIntersector$$1.COLLINEAR_INTERSECTION
95737             }
95738             if (p1q1p2 && q1p1q2) {
95739               this._intPt[0] = q1;
95740               this._intPt[1] = p1;
95741               return q1.equals(p1) && !p1q2p2 && !q1p2q2 ? LineIntersector$$1.POINT_INTERSECTION : LineIntersector$$1.COLLINEAR_INTERSECTION
95742             }
95743             if (p1q1p2 && q1p2q2) {
95744               this._intPt[0] = q1;
95745               this._intPt[1] = p2;
95746               return q1.equals(p2) && !p1q2p2 && !q1p1q2 ? LineIntersector$$1.POINT_INTERSECTION : LineIntersector$$1.COLLINEAR_INTERSECTION
95747             }
95748             if (p1q2p2 && q1p1q2) {
95749               this._intPt[0] = q2;
95750               this._intPt[1] = p1;
95751               return q2.equals(p1) && !p1q1p2 && !q1p2q2 ? LineIntersector$$1.POINT_INTERSECTION : LineIntersector$$1.COLLINEAR_INTERSECTION
95752             }
95753             if (p1q2p2 && q1p2q2) {
95754               this._intPt[0] = q2;
95755               this._intPt[1] = p2;
95756               return q2.equals(p2) && !p1q1p2 && !q1p1q2 ? LineIntersector$$1.POINT_INTERSECTION : LineIntersector$$1.COLLINEAR_INTERSECTION
95757             }
95758             return LineIntersector$$1.NO_INTERSECTION
95759           };
95760           RobustLineIntersector.prototype.normalizeToEnvCentre = function normalizeToEnvCentre (n00, n01, n10, n11, normPt) {
95761             var minX0 = n00.x < n01.x ? n00.x : n01.x;
95762             var minY0 = n00.y < n01.y ? n00.y : n01.y;
95763             var maxX0 = n00.x > n01.x ? n00.x : n01.x;
95764             var maxY0 = n00.y > n01.y ? n00.y : n01.y;
95765             var minX1 = n10.x < n11.x ? n10.x : n11.x;
95766             var minY1 = n10.y < n11.y ? n10.y : n11.y;
95767             var maxX1 = n10.x > n11.x ? n10.x : n11.x;
95768             var maxY1 = n10.y > n11.y ? n10.y : n11.y;
95769             var intMinX = minX0 > minX1 ? minX0 : minX1;
95770             var intMaxX = maxX0 < maxX1 ? maxX0 : maxX1;
95771             var intMinY = minY0 > minY1 ? minY0 : minY1;
95772             var intMaxY = maxY0 < maxY1 ? maxY0 : maxY1;
95773             var intMidX = (intMinX + intMaxX) / 2.0;
95774             var intMidY = (intMinY + intMaxY) / 2.0;
95775             normPt.x = intMidX;
95776             normPt.y = intMidY;
95777             n00.x -= normPt.x;
95778             n00.y -= normPt.y;
95779             n01.x -= normPt.x;
95780             n01.y -= normPt.y;
95781             n10.x -= normPt.x;
95782             n10.y -= normPt.y;
95783             n11.x -= normPt.x;
95784             n11.y -= normPt.y;
95785           };
95786           RobustLineIntersector.prototype.computeIntersect = function computeIntersect (p1, p2, q1, q2) {
95787             this._isProper = false;
95788             if (!Envelope.intersects(p1, p2, q1, q2)) { return LineIntersector$$1.NO_INTERSECTION }
95789             var Pq1 = CGAlgorithms.orientationIndex(p1, p2, q1);
95790             var Pq2 = CGAlgorithms.orientationIndex(p1, p2, q2);
95791             if ((Pq1 > 0 && Pq2 > 0) || (Pq1 < 0 && Pq2 < 0)) {
95792               return LineIntersector$$1.NO_INTERSECTION
95793             }
95794             var Qp1 = CGAlgorithms.orientationIndex(q1, q2, p1);
95795             var Qp2 = CGAlgorithms.orientationIndex(q1, q2, p2);
95796             if ((Qp1 > 0 && Qp2 > 0) || (Qp1 < 0 && Qp2 < 0)) {
95797               return LineIntersector$$1.NO_INTERSECTION
95798             }
95799             var collinear = Pq1 === 0 && Pq2 === 0 && Qp1 === 0 && Qp2 === 0;
95800             if (collinear) {
95801               return this.computeCollinearIntersection(p1, p2, q1, q2)
95802             }
95803             if (Pq1 === 0 || Pq2 === 0 || Qp1 === 0 || Qp2 === 0) {
95804               this._isProper = false;
95805               if (p1.equals2D(q1) || p1.equals2D(q2)) {
95806                 this._intPt[0] = p1;
95807               } else if (p2.equals2D(q1) || p2.equals2D(q2)) {
95808                 this._intPt[0] = p2;
95809               } else if (Pq1 === 0) {
95810                 this._intPt[0] = new Coordinate(q1);
95811               } else if (Pq2 === 0) {
95812                 this._intPt[0] = new Coordinate(q2);
95813               } else if (Qp1 === 0) {
95814                 this._intPt[0] = new Coordinate(p1);
95815               } else if (Qp2 === 0) {
95816                 this._intPt[0] = new Coordinate(p2);
95817               }
95818             } else {
95819               this._isProper = true;
95820               this._intPt[0] = this.intersection(p1, p2, q1, q2);
95821             }
95822             return LineIntersector$$1.POINT_INTERSECTION
95823           };
95824           RobustLineIntersector.prototype.interfaces_ = function interfaces_ () {
95825             return []
95826           };
95827           RobustLineIntersector.prototype.getClass = function getClass () {
95828             return RobustLineIntersector
95829           };
95830           RobustLineIntersector.nearestEndpoint = function nearestEndpoint (p1, p2, q1, q2) {
95831             var nearestPt = p1;
95832             var minDist = CGAlgorithms.distancePointLine(p1, q1, q2);
95833             var dist = CGAlgorithms.distancePointLine(p2, q1, q2);
95834             if (dist < minDist) {
95835               minDist = dist;
95836               nearestPt = p2;
95837             }
95838             dist = CGAlgorithms.distancePointLine(q1, p1, p2);
95839             if (dist < minDist) {
95840               minDist = dist;
95841               nearestPt = q1;
95842             }
95843             dist = CGAlgorithms.distancePointLine(q2, p1, p2);
95844             if (dist < minDist) {
95845               minDist = dist;
95846               nearestPt = q2;
95847             }
95848             return nearestPt
95849           };
95850
95851           return RobustLineIntersector;
95852         }(LineIntersector));
95853
95854         var RobustDeterminant = function RobustDeterminant () {};
95855
95856         RobustDeterminant.prototype.interfaces_ = function interfaces_ () {
95857           return []
95858         };
95859         RobustDeterminant.prototype.getClass = function getClass () {
95860           return RobustDeterminant
95861         };
95862         RobustDeterminant.orientationIndex = function orientationIndex (p1, p2, q) {
95863           var dx1 = p2.x - p1.x;
95864           var dy1 = p2.y - p1.y;
95865           var dx2 = q.x - p2.x;
95866           var dy2 = q.y - p2.y;
95867           return RobustDeterminant.signOfDet2x2(dx1, dy1, dx2, dy2)
95868         };
95869         RobustDeterminant.signOfDet2x2 = function signOfDet2x2 (x1, y1, x2, y2) {
95870           var sign = null;
95871           var swap = null;
95872           var k = null;
95873           sign = 1;
95874           if (x1 === 0.0 || y2 === 0.0) {
95875             if (y1 === 0.0 || x2 === 0.0) {
95876               return 0
95877             } else if (y1 > 0) {
95878               if (x2 > 0) {
95879                 return -sign
95880               } else {
95881                 return sign
95882               }
95883             } else {
95884               if (x2 > 0) {
95885                 return sign
95886               } else {
95887                 return -sign
95888               }
95889             }
95890           }
95891           if (y1 === 0.0 || x2 === 0.0) {
95892             if (y2 > 0) {
95893               if (x1 > 0) {
95894                 return sign
95895               } else {
95896                 return -sign
95897               }
95898             } else {
95899               if (x1 > 0) {
95900                 return -sign
95901               } else {
95902                 return sign
95903               }
95904             }
95905           }
95906           if (y1 > 0.0) {
95907             if (y2 > 0.0) {
95908               if (y1 <= y2) ; else {
95909                 sign = -sign;
95910                 swap = x1;
95911                 x1 = x2;
95912                 x2 = swap;
95913                 swap = y1;
95914                 y1 = y2;
95915                 y2 = swap;
95916               }
95917             } else {
95918               if (y1 <= -y2) {
95919                 sign = -sign;
95920                 x2 = -x2;
95921                 y2 = -y2;
95922               } else {
95923                 swap = x1;
95924                 x1 = -x2;
95925                 x2 = swap;
95926                 swap = y1;
95927                 y1 = -y2;
95928                 y2 = swap;
95929               }
95930             }
95931           } else {
95932             if (y2 > 0.0) {
95933               if (-y1 <= y2) {
95934                 sign = -sign;
95935                 x1 = -x1;
95936                 y1 = -y1;
95937               } else {
95938                 swap = -x1;
95939                 x1 = x2;
95940                 x2 = swap;
95941                 swap = -y1;
95942                 y1 = y2;
95943                 y2 = swap;
95944               }
95945             } else {
95946               if (y1 >= y2) {
95947                 x1 = -x1;
95948                 y1 = -y1;
95949                 x2 = -x2;
95950                 y2 = -y2;
95951               } else {
95952                 sign = -sign;
95953                 swap = -x1;
95954                 x1 = -x2;
95955                 x2 = swap;
95956                 swap = -y1;
95957                 y1 = -y2;
95958                 y2 = swap;
95959               }
95960             }
95961           }
95962           if (x1 > 0.0) {
95963             if (x2 > 0.0) {
95964               if (x1 <= x2) ; else {
95965                 return sign
95966               }
95967             } else {
95968               return sign
95969             }
95970           } else {
95971             if (x2 > 0.0) {
95972               return -sign
95973             } else {
95974               if (x1 >= x2) {
95975                 sign = -sign;
95976                 x1 = -x1;
95977                 x2 = -x2;
95978               } else {
95979                 return -sign
95980               }
95981             }
95982           }
95983           while (true) {
95984             k = Math.floor(x2 / x1);
95985             x2 = x2 - k * x1;
95986             y2 = y2 - k * y1;
95987             if (y2 < 0.0) {
95988               return -sign
95989             }
95990             if (y2 > y1) {
95991               return sign
95992             }
95993             if (x1 > x2 + x2) {
95994               if (y1 < y2 + y2) {
95995                 return sign
95996               }
95997             } else {
95998               if (y1 > y2 + y2) {
95999                 return -sign
96000               } else {
96001                 x2 = x1 - x2;
96002                 y2 = y1 - y2;
96003                 sign = -sign;
96004               }
96005             }
96006             if (y2 === 0.0) {
96007               if (x2 === 0.0) {
96008                 return 0
96009               } else {
96010                 return -sign
96011               }
96012             }
96013             if (x2 === 0.0) {
96014               return sign
96015             }
96016             k = Math.floor(x1 / x2);
96017             x1 = x1 - k * x2;
96018             y1 = y1 - k * y2;
96019             if (y1 < 0.0) {
96020               return sign
96021             }
96022             if (y1 > y2) {
96023               return -sign
96024             }
96025             if (x2 > x1 + x1) {
96026               if (y2 < y1 + y1) {
96027                 return -sign
96028               }
96029             } else {
96030               if (y2 > y1 + y1) {
96031                 return sign
96032               } else {
96033                 x1 = x2 - x1;
96034                 y1 = y2 - y1;
96035                 sign = -sign;
96036               }
96037             }
96038             if (y1 === 0.0) {
96039               if (x1 === 0.0) {
96040                 return 0
96041               } else {
96042                 return sign
96043               }
96044             }
96045             if (x1 === 0.0) {
96046               return -sign
96047             }
96048           }
96049         };
96050
96051         var RayCrossingCounter = function RayCrossingCounter () {
96052           this._p = null;
96053           this._crossingCount = 0;
96054           this._isPointOnSegment = false;
96055           var p = arguments[0];
96056           this._p = p;
96057         };
96058         RayCrossingCounter.prototype.countSegment = function countSegment (p1, p2) {
96059           if (p1.x < this._p.x && p2.x < this._p.x) { return null }
96060           if (this._p.x === p2.x && this._p.y === p2.y) {
96061             this._isPointOnSegment = true;
96062             return null
96063           }
96064           if (p1.y === this._p.y && p2.y === this._p.y) {
96065             var minx = p1.x;
96066             var maxx = p2.x;
96067             if (minx > maxx) {
96068               minx = p2.x;
96069               maxx = p1.x;
96070             }
96071             if (this._p.x >= minx && this._p.x <= maxx) {
96072               this._isPointOnSegment = true;
96073             }
96074             return null
96075           }
96076           if ((p1.y > this._p.y && p2.y <= this._p.y) || (p2.y > this._p.y && p1.y <= this._p.y)) {
96077             var x1 = p1.x - this._p.x;
96078             var y1 = p1.y - this._p.y;
96079             var x2 = p2.x - this._p.x;
96080             var y2 = p2.y - this._p.y;
96081             var xIntSign = RobustDeterminant.signOfDet2x2(x1, y1, x2, y2);
96082             if (xIntSign === 0.0) {
96083               this._isPointOnSegment = true;
96084               return null
96085             }
96086             if (y2 < y1) { xIntSign = -xIntSign; }
96087             if (xIntSign > 0.0) {
96088               this._crossingCount++;
96089             }
96090           }
96091         };
96092         RayCrossingCounter.prototype.isPointInPolygon = function isPointInPolygon () {
96093           return this.getLocation() !== Location.EXTERIOR
96094         };
96095         RayCrossingCounter.prototype.getLocation = function getLocation () {
96096           if (this._isPointOnSegment) { return Location.BOUNDARY }
96097           if (this._crossingCount % 2 === 1) {
96098             return Location.INTERIOR
96099           }
96100           return Location.EXTERIOR
96101         };
96102         RayCrossingCounter.prototype.isOnSegment = function isOnSegment () {
96103           return this._isPointOnSegment
96104         };
96105         RayCrossingCounter.prototype.interfaces_ = function interfaces_ () {
96106           return []
96107         };
96108         RayCrossingCounter.prototype.getClass = function getClass () {
96109           return RayCrossingCounter
96110         };
96111         RayCrossingCounter.locatePointInRing = function locatePointInRing () {
96112           if (arguments[0] instanceof Coordinate && hasInterface(arguments[1], CoordinateSequence)) {
96113             var p = arguments[0];
96114             var ring = arguments[1];
96115             var counter = new RayCrossingCounter(p);
96116             var p1 = new Coordinate();
96117             var p2 = new Coordinate();
96118             for (var i = 1; i < ring.size(); i++) {
96119               ring.getCoordinate(i, p1);
96120               ring.getCoordinate(i - 1, p2);
96121               counter.countSegment(p1, p2);
96122               if (counter.isOnSegment()) { return counter.getLocation() }
96123             }
96124             return counter.getLocation()
96125           } else if (arguments[0] instanceof Coordinate && arguments[1] instanceof Array) {
96126             var p$1 = arguments[0];
96127             var ring$1 = arguments[1];
96128             var counter$1 = new RayCrossingCounter(p$1);
96129             for (var i$1 = 1; i$1 < ring$1.length; i$1++) {
96130               var p1$1 = ring$1[i$1];
96131               var p2$1 = ring$1[i$1 - 1];
96132               counter$1.countSegment(p1$1, p2$1);
96133               if (counter$1.isOnSegment()) { return counter$1.getLocation() }
96134             }
96135             return counter$1.getLocation()
96136           }
96137         };
96138
96139         var CGAlgorithms = function CGAlgorithms () {};
96140
96141         var staticAccessors$3 = { CLOCKWISE: { configurable: true },RIGHT: { configurable: true },COUNTERCLOCKWISE: { configurable: true },LEFT: { configurable: true },COLLINEAR: { configurable: true },STRAIGHT: { configurable: true } };
96142
96143         CGAlgorithms.prototype.interfaces_ = function interfaces_ () {
96144           return []
96145         };
96146         CGAlgorithms.prototype.getClass = function getClass () {
96147           return CGAlgorithms
96148         };
96149         CGAlgorithms.orientationIndex = function orientationIndex (p1, p2, q) {
96150           return CGAlgorithmsDD.orientationIndex(p1, p2, q)
96151         };
96152         CGAlgorithms.signedArea = function signedArea () {
96153           if (arguments[0] instanceof Array) {
96154             var ring = arguments[0];
96155             if (ring.length < 3) { return 0.0 }
96156             var sum = 0.0;
96157             var x0 = ring[0].x;
96158             for (var i = 1; i < ring.length - 1; i++) {
96159               var x = ring[i].x - x0;
96160               var y1 = ring[i + 1].y;
96161               var y2 = ring[i - 1].y;
96162               sum += x * (y2 - y1);
96163             }
96164             return sum / 2.0
96165           } else if (hasInterface(arguments[0], CoordinateSequence)) {
96166             var ring$1 = arguments[0];
96167             var n = ring$1.size();
96168             if (n < 3) { return 0.0 }
96169             var p0 = new Coordinate();
96170             var p1 = new Coordinate();
96171             var p2 = new Coordinate();
96172             ring$1.getCoordinate(0, p1);
96173             ring$1.getCoordinate(1, p2);
96174             var x0$1 = p1.x;
96175             p2.x -= x0$1;
96176             var sum$1 = 0.0;
96177             for (var i$1 = 1; i$1 < n - 1; i$1++) {
96178               p0.y = p1.y;
96179               p1.x = p2.x;
96180               p1.y = p2.y;
96181               ring$1.getCoordinate(i$1 + 1, p2);
96182               p2.x -= x0$1;
96183               sum$1 += p1.x * (p0.y - p2.y);
96184             }
96185             return sum$1 / 2.0
96186           }
96187         };
96188         CGAlgorithms.distanceLineLine = function distanceLineLine (A, B, C, D) {
96189           if (A.equals(B)) { return CGAlgorithms.distancePointLine(A, C, D) }
96190           if (C.equals(D)) { return CGAlgorithms.distancePointLine(D, A, B) }
96191           var noIntersection = false;
96192           if (!Envelope.intersects(A, B, C, D)) {
96193             noIntersection = true;
96194           } else {
96195             var denom = (B.x - A.x) * (D.y - C.y) - (B.y - A.y) * (D.x - C.x);
96196             if (denom === 0) {
96197               noIntersection = true;
96198             } else {
96199               var rNumb = (A.y - C.y) * (D.x - C.x) - (A.x - C.x) * (D.y - C.y);
96200               var sNum = (A.y - C.y) * (B.x - A.x) - (A.x - C.x) * (B.y - A.y);
96201               var s = sNum / denom;
96202               var r = rNumb / denom;
96203               if (r < 0 || r > 1 || s < 0 || s > 1) {
96204                 noIntersection = true;
96205               }
96206             }
96207           }
96208           if (noIntersection) {
96209             return MathUtil.min(CGAlgorithms.distancePointLine(A, C, D), CGAlgorithms.distancePointLine(B, C, D), CGAlgorithms.distancePointLine(C, A, B), CGAlgorithms.distancePointLine(D, A, B))
96210           }
96211           return 0.0
96212         };
96213         CGAlgorithms.isPointInRing = function isPointInRing (p, ring) {
96214           return CGAlgorithms.locatePointInRing(p, ring) !== Location.EXTERIOR
96215         };
96216         CGAlgorithms.computeLength = function computeLength (pts) {
96217           var n = pts.size();
96218           if (n <= 1) { return 0.0 }
96219           var len = 0.0;
96220           var p = new Coordinate();
96221           pts.getCoordinate(0, p);
96222           var x0 = p.x;
96223           var y0 = p.y;
96224           for (var i = 1; i < n; i++) {
96225             pts.getCoordinate(i, p);
96226             var x1 = p.x;
96227             var y1 = p.y;
96228             var dx = x1 - x0;
96229             var dy = y1 - y0;
96230             len += Math.sqrt(dx * dx + dy * dy);
96231             x0 = x1;
96232             y0 = y1;
96233           }
96234           return len
96235         };
96236         CGAlgorithms.isCCW = function isCCW (ring) {
96237           var nPts = ring.length - 1;
96238           if (nPts < 3) { throw new IllegalArgumentException('Ring has fewer than 4 points, so orientation cannot be determined') }
96239           var hiPt = ring[0];
96240           var hiIndex = 0;
96241           for (var i = 1; i <= nPts; i++) {
96242             var p = ring[i];
96243             if (p.y > hiPt.y) {
96244               hiPt = p;
96245               hiIndex = i;
96246             }
96247           }
96248           var iPrev = hiIndex;
96249           do {
96250             iPrev = iPrev - 1;
96251             if (iPrev < 0) { iPrev = nPts; }
96252           } while (ring[iPrev].equals2D(hiPt) && iPrev !== hiIndex)
96253           var iNext = hiIndex;
96254           do {
96255             iNext = (iNext + 1) % nPts;
96256           } while (ring[iNext].equals2D(hiPt) && iNext !== hiIndex)
96257           var prev = ring[iPrev];
96258           var next = ring[iNext];
96259           if (prev.equals2D(hiPt) || next.equals2D(hiPt) || prev.equals2D(next)) { return false }
96260           var disc = CGAlgorithms.computeOrientation(prev, hiPt, next);
96261           var isCCW = false;
96262           if (disc === 0) {
96263             isCCW = prev.x > next.x;
96264           } else {
96265             isCCW = disc > 0;
96266           }
96267           return isCCW
96268         };
96269         CGAlgorithms.locatePointInRing = function locatePointInRing (p, ring) {
96270           return RayCrossingCounter.locatePointInRing(p, ring)
96271         };
96272         CGAlgorithms.distancePointLinePerpendicular = function distancePointLinePerpendicular (p, A, B) {
96273           var len2 = (B.x - A.x) * (B.x - A.x) + (B.y - A.y) * (B.y - A.y);
96274           var s = ((A.y - p.y) * (B.x - A.x) - (A.x - p.x) * (B.y - A.y)) / len2;
96275           return Math.abs(s) * Math.sqrt(len2)
96276         };
96277         CGAlgorithms.computeOrientation = function computeOrientation (p1, p2, q) {
96278           return CGAlgorithms.orientationIndex(p1, p2, q)
96279         };
96280         CGAlgorithms.distancePointLine = function distancePointLine () {
96281           if (arguments.length === 2) {
96282             var p = arguments[0];
96283             var line = arguments[1];
96284             if (line.length === 0) { throw new IllegalArgumentException('Line array must contain at least one vertex') }
96285             var minDistance = p.distance(line[0]);
96286             for (var i = 0; i < line.length - 1; i++) {
96287               var dist = CGAlgorithms.distancePointLine(p, line[i], line[i + 1]);
96288               if (dist < minDistance) {
96289                 minDistance = dist;
96290               }
96291             }
96292             return minDistance
96293           } else if (arguments.length === 3) {
96294             var p$1 = arguments[0];
96295             var A = arguments[1];
96296             var B = arguments[2];
96297             if (A.x === B.x && A.y === B.y) { return p$1.distance(A) }
96298             var len2 = (B.x - A.x) * (B.x - A.x) + (B.y - A.y) * (B.y - A.y);
96299             var r = ((p$1.x - A.x) * (B.x - A.x) + (p$1.y - A.y) * (B.y - A.y)) / len2;
96300             if (r <= 0.0) { return p$1.distance(A) }
96301             if (r >= 1.0) { return p$1.distance(B) }
96302             var s = ((A.y - p$1.y) * (B.x - A.x) - (A.x - p$1.x) * (B.y - A.y)) / len2;
96303             return Math.abs(s) * Math.sqrt(len2)
96304           }
96305         };
96306         CGAlgorithms.isOnLine = function isOnLine (p, pt) {
96307           var lineIntersector = new RobustLineIntersector();
96308           for (var i = 1; i < pt.length; i++) {
96309             var p0 = pt[i - 1];
96310             var p1 = pt[i];
96311             lineIntersector.computeIntersection(p, p0, p1);
96312             if (lineIntersector.hasIntersection()) {
96313               return true
96314             }
96315           }
96316           return false
96317         };
96318         staticAccessors$3.CLOCKWISE.get = function () { return -1 };
96319         staticAccessors$3.RIGHT.get = function () { return CGAlgorithms.CLOCKWISE };
96320         staticAccessors$3.COUNTERCLOCKWISE.get = function () { return 1 };
96321         staticAccessors$3.LEFT.get = function () { return CGAlgorithms.COUNTERCLOCKWISE };
96322         staticAccessors$3.COLLINEAR.get = function () { return 0 };
96323         staticAccessors$3.STRAIGHT.get = function () { return CGAlgorithms.COLLINEAR };
96324
96325         Object.defineProperties( CGAlgorithms, staticAccessors$3 );
96326
96327         var GeometryComponentFilter = function GeometryComponentFilter () {};
96328
96329         GeometryComponentFilter.prototype.filter = function filter (geom) {};
96330         GeometryComponentFilter.prototype.interfaces_ = function interfaces_ () {
96331           return []
96332         };
96333         GeometryComponentFilter.prototype.getClass = function getClass () {
96334           return GeometryComponentFilter
96335         };
96336
96337         var Geometry = function Geometry () {
96338           var factory = arguments[0];
96339
96340           this._envelope = null;
96341           this._factory = null;
96342           this._SRID = null;
96343           this._userData = null;
96344           this._factory = factory;
96345           this._SRID = factory.getSRID();
96346         };
96347
96348         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 } };
96349         Geometry.prototype.isGeometryCollection = function isGeometryCollection () {
96350           return this.getSortIndex() === Geometry.SORTINDEX_GEOMETRYCOLLECTION
96351         };
96352         Geometry.prototype.getFactory = function getFactory () {
96353           return this._factory
96354         };
96355         Geometry.prototype.getGeometryN = function getGeometryN (n) {
96356           return this
96357         };
96358         Geometry.prototype.getArea = function getArea () {
96359           return 0.0
96360         };
96361         Geometry.prototype.isRectangle = function isRectangle () {
96362           return false
96363         };
96364         Geometry.prototype.equals = function equals () {
96365           if (arguments[0] instanceof Geometry) {
96366             var g$1 = arguments[0];
96367             if (g$1 === null) { return false }
96368             return this.equalsTopo(g$1)
96369           } else if (arguments[0] instanceof Object) {
96370             var o = arguments[0];
96371             if (!(o instanceof Geometry)) { return false }
96372             var g = o;
96373             return this.equalsExact(g)
96374           }
96375         };
96376         Geometry.prototype.equalsExact = function equalsExact (other) {
96377           return this === other || this.equalsExact(other, 0)
96378         };
96379         Geometry.prototype.geometryChanged = function geometryChanged () {
96380           this.apply(Geometry.geometryChangedFilter);
96381         };
96382         Geometry.prototype.geometryChangedAction = function geometryChangedAction () {
96383           this._envelope = null;
96384         };
96385         Geometry.prototype.equalsNorm = function equalsNorm (g) {
96386           if (g === null) { return false }
96387           return this.norm().equalsExact(g.norm())
96388         };
96389         Geometry.prototype.getLength = function getLength () {
96390           return 0.0
96391         };
96392         Geometry.prototype.getNumGeometries = function getNumGeometries () {
96393           return 1
96394         };
96395         Geometry.prototype.compareTo = function compareTo () {
96396           if (arguments.length === 1) {
96397             var o = arguments[0];
96398             var other = o;
96399             if (this.getSortIndex() !== other.getSortIndex()) {
96400               return this.getSortIndex() - other.getSortIndex()
96401             }
96402             if (this.isEmpty() && other.isEmpty()) {
96403               return 0
96404             }
96405             if (this.isEmpty()) {
96406               return -1
96407             }
96408             if (other.isEmpty()) {
96409               return 1
96410             }
96411             return this.compareToSameClass(o)
96412           } else if (arguments.length === 2) {
96413             var other$1 = arguments[0];
96414             var comp = arguments[1];
96415             if (this.getSortIndex() !== other$1.getSortIndex()) {
96416               return this.getSortIndex() - other$1.getSortIndex()
96417             }
96418             if (this.isEmpty() && other$1.isEmpty()) {
96419               return 0
96420             }
96421             if (this.isEmpty()) {
96422               return -1
96423             }
96424             if (other$1.isEmpty()) {
96425               return 1
96426             }
96427             return this.compareToSameClass(other$1, comp)
96428           }
96429         };
96430         Geometry.prototype.getUserData = function getUserData () {
96431           return this._userData
96432         };
96433         Geometry.prototype.getSRID = function getSRID () {
96434           return this._SRID
96435         };
96436         Geometry.prototype.getEnvelope = function getEnvelope () {
96437           return this.getFactory().toGeometry(this.getEnvelopeInternal())
96438         };
96439         Geometry.prototype.checkNotGeometryCollection = function checkNotGeometryCollection (g) {
96440           if (g.getSortIndex() === Geometry.SORTINDEX_GEOMETRYCOLLECTION) {
96441             throw new IllegalArgumentException('This method does not support GeometryCollection arguments')
96442           }
96443         };
96444         Geometry.prototype.equal = function equal (a, b, tolerance) {
96445           if (tolerance === 0) {
96446             return a.equals(b)
96447           }
96448           return a.distance(b) <= tolerance
96449         };
96450         Geometry.prototype.norm = function norm () {
96451           var copy = this.copy();
96452           copy.normalize();
96453           return copy
96454         };
96455         Geometry.prototype.getPrecisionModel = function getPrecisionModel () {
96456           return this._factory.getPrecisionModel()
96457         };
96458         Geometry.prototype.getEnvelopeInternal = function getEnvelopeInternal () {
96459           if (this._envelope === null) {
96460             this._envelope = this.computeEnvelopeInternal();
96461           }
96462           return new Envelope(this._envelope)
96463         };
96464         Geometry.prototype.setSRID = function setSRID (SRID) {
96465           this._SRID = SRID;
96466         };
96467         Geometry.prototype.setUserData = function setUserData (userData) {
96468           this._userData = userData;
96469         };
96470         Geometry.prototype.compare = function compare (a, b) {
96471           var i = a.iterator();
96472           var j = b.iterator();
96473           while (i.hasNext() && j.hasNext()) {
96474             var aElement = i.next();
96475             var bElement = j.next();
96476             var comparison = aElement.compareTo(bElement);
96477             if (comparison !== 0) {
96478               return comparison
96479             }
96480           }
96481           if (i.hasNext()) {
96482             return 1
96483           }
96484           if (j.hasNext()) {
96485             return -1
96486           }
96487           return 0
96488         };
96489         Geometry.prototype.hashCode = function hashCode () {
96490           return this.getEnvelopeInternal().hashCode()
96491         };
96492         Geometry.prototype.isGeometryCollectionOrDerived = function isGeometryCollectionOrDerived () {
96493           if (this.getSortIndex() === Geometry.SORTINDEX_GEOMETRYCOLLECTION || this.getSortIndex() === Geometry.SORTINDEX_MULTIPOINT || this.getSortIndex() === Geometry.SORTINDEX_MULTILINESTRING || this.getSortIndex() === Geometry.SORTINDEX_MULTIPOLYGON) {
96494             return true
96495           }
96496           return false
96497         };
96498         Geometry.prototype.interfaces_ = function interfaces_ () {
96499           return [Clonable, Comparable, Serializable]
96500         };
96501         Geometry.prototype.getClass = function getClass () {
96502           return Geometry
96503         };
96504         Geometry.hasNonEmptyElements = function hasNonEmptyElements (geometries) {
96505           for (var i = 0; i < geometries.length; i++) {
96506             if (!geometries[i].isEmpty()) {
96507               return true
96508             }
96509           }
96510           return false
96511         };
96512         Geometry.hasNullElements = function hasNullElements (array) {
96513           for (var i = 0; i < array.length; i++) {
96514             if (array[i] === null) {
96515               return true
96516             }
96517           }
96518           return false
96519         };
96520         staticAccessors$11.serialVersionUID.get = function () { return 8763622679187376702 };
96521         staticAccessors$11.SORTINDEX_POINT.get = function () { return 0 };
96522         staticAccessors$11.SORTINDEX_MULTIPOINT.get = function () { return 1 };
96523         staticAccessors$11.SORTINDEX_LINESTRING.get = function () { return 2 };
96524         staticAccessors$11.SORTINDEX_LINEARRING.get = function () { return 3 };
96525         staticAccessors$11.SORTINDEX_MULTILINESTRING.get = function () { return 4 };
96526         staticAccessors$11.SORTINDEX_POLYGON.get = function () { return 5 };
96527         staticAccessors$11.SORTINDEX_MULTIPOLYGON.get = function () { return 6 };
96528         staticAccessors$11.SORTINDEX_GEOMETRYCOLLECTION.get = function () { return 7 };
96529         staticAccessors$11.geometryChangedFilter.get = function () { return geometryChangedFilter };
96530
96531         Object.defineProperties( Geometry, staticAccessors$11 );
96532
96533         var geometryChangedFilter = function geometryChangedFilter () {};
96534
96535         geometryChangedFilter.interfaces_ = function interfaces_ () {
96536           return [GeometryComponentFilter]
96537         };
96538         geometryChangedFilter.filter = function filter (geom) {
96539           geom.geometryChangedAction();
96540         };
96541
96542         var CoordinateFilter = function CoordinateFilter () {};
96543
96544         CoordinateFilter.prototype.filter = function filter (coord) {};
96545         CoordinateFilter.prototype.interfaces_ = function interfaces_ () {
96546           return []
96547         };
96548         CoordinateFilter.prototype.getClass = function getClass () {
96549           return CoordinateFilter
96550         };
96551
96552         var BoundaryNodeRule = function BoundaryNodeRule () {};
96553
96554         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 } };
96555
96556         BoundaryNodeRule.prototype.isInBoundary = function isInBoundary (boundaryCount) {};
96557         BoundaryNodeRule.prototype.interfaces_ = function interfaces_ () {
96558           return []
96559         };
96560         BoundaryNodeRule.prototype.getClass = function getClass () {
96561           return BoundaryNodeRule
96562         };
96563         staticAccessors$12.Mod2BoundaryNodeRule.get = function () { return Mod2BoundaryNodeRule };
96564         staticAccessors$12.EndPointBoundaryNodeRule.get = function () { return EndPointBoundaryNodeRule };
96565         staticAccessors$12.MultiValentEndPointBoundaryNodeRule.get = function () { return MultiValentEndPointBoundaryNodeRule };
96566         staticAccessors$12.MonoValentEndPointBoundaryNodeRule.get = function () { return MonoValentEndPointBoundaryNodeRule };
96567         staticAccessors$12.MOD2_BOUNDARY_RULE.get = function () { return new Mod2BoundaryNodeRule() };
96568         staticAccessors$12.ENDPOINT_BOUNDARY_RULE.get = function () { return new EndPointBoundaryNodeRule() };
96569         staticAccessors$12.MULTIVALENT_ENDPOINT_BOUNDARY_RULE.get = function () { return new MultiValentEndPointBoundaryNodeRule() };
96570         staticAccessors$12.MONOVALENT_ENDPOINT_BOUNDARY_RULE.get = function () { return new MonoValentEndPointBoundaryNodeRule() };
96571         staticAccessors$12.OGC_SFS_BOUNDARY_RULE.get = function () { return BoundaryNodeRule.MOD2_BOUNDARY_RULE };
96572
96573         Object.defineProperties( BoundaryNodeRule, staticAccessors$12 );
96574
96575         var Mod2BoundaryNodeRule = function Mod2BoundaryNodeRule () {};
96576
96577         Mod2BoundaryNodeRule.prototype.isInBoundary = function isInBoundary (boundaryCount) {
96578           return boundaryCount % 2 === 1
96579         };
96580         Mod2BoundaryNodeRule.prototype.interfaces_ = function interfaces_ () {
96581           return [BoundaryNodeRule]
96582         };
96583         Mod2BoundaryNodeRule.prototype.getClass = function getClass () {
96584           return Mod2BoundaryNodeRule
96585         };
96586
96587         var EndPointBoundaryNodeRule = function EndPointBoundaryNodeRule () {};
96588
96589         EndPointBoundaryNodeRule.prototype.isInBoundary = function isInBoundary (boundaryCount) {
96590           return boundaryCount > 0
96591         };
96592         EndPointBoundaryNodeRule.prototype.interfaces_ = function interfaces_ () {
96593           return [BoundaryNodeRule]
96594         };
96595         EndPointBoundaryNodeRule.prototype.getClass = function getClass () {
96596           return EndPointBoundaryNodeRule
96597         };
96598
96599         var MultiValentEndPointBoundaryNodeRule = function MultiValentEndPointBoundaryNodeRule () {};
96600
96601         MultiValentEndPointBoundaryNodeRule.prototype.isInBoundary = function isInBoundary (boundaryCount) {
96602           return boundaryCount > 1
96603         };
96604         MultiValentEndPointBoundaryNodeRule.prototype.interfaces_ = function interfaces_ () {
96605           return [BoundaryNodeRule]
96606         };
96607         MultiValentEndPointBoundaryNodeRule.prototype.getClass = function getClass () {
96608           return MultiValentEndPointBoundaryNodeRule
96609         };
96610
96611         var MonoValentEndPointBoundaryNodeRule = function MonoValentEndPointBoundaryNodeRule () {};
96612
96613         MonoValentEndPointBoundaryNodeRule.prototype.isInBoundary = function isInBoundary (boundaryCount) {
96614           return boundaryCount === 1
96615         };
96616         MonoValentEndPointBoundaryNodeRule.prototype.interfaces_ = function interfaces_ () {
96617           return [BoundaryNodeRule]
96618         };
96619         MonoValentEndPointBoundaryNodeRule.prototype.getClass = function getClass () {
96620           return MonoValentEndPointBoundaryNodeRule
96621         };
96622
96623         // import Iterator from './Iterator'
96624
96625         /**
96626          * @see http://download.oracle.com/javase/6/docs/api/java/util/Collection.html
96627          *
96628          * @constructor
96629          * @private
96630          */
96631         var Collection = function Collection () {};
96632
96633         Collection.prototype.add = function add () {};
96634
96635         /**
96636          * Appends all of the elements in the specified collection to the end of this
96637          * list, in the order that they are returned by the specified collection's
96638          * iterator (optional operation).
96639          * @param {javascript.util.Collection} c
96640          * @return {boolean}
96641          */
96642         Collection.prototype.addAll = function addAll () {};
96643
96644         /**
96645          * Returns true if this collection contains no elements.
96646          * @return {boolean}
96647          */
96648         Collection.prototype.isEmpty = function isEmpty () {};
96649
96650         /**
96651          * Returns an iterator over the elements in this collection.
96652          * @return {javascript.util.Iterator}
96653          */
96654         Collection.prototype.iterator = function iterator () {};
96655
96656         /**
96657          * Returns an iterator over the elements in this collection.
96658          * @return {number}
96659          */
96660         Collection.prototype.size = function size () {};
96661
96662         /**
96663          * Returns an array containing all of the elements in this collection.
96664          * @return {Array}
96665          */
96666         Collection.prototype.toArray = function toArray () {};
96667
96668         /**
96669          * Removes a single instance of the specified element from this collection if it
96670          * is present. (optional)
96671          * @param {Object} e
96672          * @return {boolean}
96673          */
96674         Collection.prototype.remove = function remove () {};
96675
96676         /**
96677          * @param {string=} message Optional message
96678          * @extends {Error}
96679          * @constructor
96680          * @private
96681          */
96682         function IndexOutOfBoundsException (message) {
96683           this.message = message || '';
96684         }
96685         IndexOutOfBoundsException.prototype = new Error();
96686
96687         /**
96688          * @type {string}
96689          */
96690         IndexOutOfBoundsException.prototype.name = 'IndexOutOfBoundsException';
96691
96692         /**
96693          * @see http://download.oracle.com/javase/6/docs/api/java/util/Iterator.html
96694          * @constructor
96695          * @private
96696          */
96697         var Iterator$1 = function Iterator () {};
96698
96699         Iterator$1.prototype.hasNext = function hasNext () {};
96700
96701         /**
96702          * Returns the next element in the iteration.
96703          * @return {Object}
96704          */
96705         Iterator$1.prototype.next = function next () {};
96706
96707         /**
96708          * Removes from the underlying collection the last element returned by the
96709          * iterator (optional operation).
96710          */
96711         Iterator$1.prototype.remove = function remove () {};
96712
96713         /**
96714          * @see http://download.oracle.com/javase/6/docs/api/java/util/List.html
96715          *
96716          * @extends {javascript.util.Collection}
96717          * @constructor
96718          * @private
96719          */
96720         var List = (function (Collection$$1) {
96721           function List () {
96722             Collection$$1.apply(this, arguments);
96723           }
96724
96725           if ( Collection$$1 ) { List.__proto__ = Collection$$1; }
96726           List.prototype = Object.create( Collection$$1 && Collection$$1.prototype );
96727           List.prototype.constructor = List;
96728
96729           List.prototype.get = function get () { };
96730
96731           /**
96732            * Replaces the element at the specified position in this list with the
96733            * specified element (optional operation).
96734            * @param {number} index
96735            * @param {Object} e
96736            * @return {Object}
96737            */
96738           List.prototype.set = function set () { };
96739
96740           /**
96741            * Returns true if this collection contains no elements.
96742            * @return {boolean}
96743            */
96744           List.prototype.isEmpty = function isEmpty () { };
96745
96746           return List;
96747         }(Collection));
96748
96749         /**
96750          * @param {string=} message Optional message
96751          * @extends {Error}
96752          * @constructor
96753          * @private
96754          */
96755         function NoSuchElementException (message) {
96756           this.message = message || '';
96757         }
96758         NoSuchElementException.prototype = new Error();
96759
96760         /**
96761          * @type {string}
96762          */
96763         NoSuchElementException.prototype.name = 'NoSuchElementException';
96764
96765         // import OperationNotSupported from './OperationNotSupported'
96766
96767         /**
96768          * @see http://download.oracle.com/javase/6/docs/api/java/util/ArrayList.html
96769          *
96770          * @extends List
96771          * @private
96772          */
96773         var ArrayList = (function (List$$1) {
96774           function ArrayList () {
96775             List$$1.call(this);
96776             this.array_ = [];
96777
96778             if (arguments[0] instanceof Collection) {
96779               this.addAll(arguments[0]);
96780             }
96781           }
96782
96783           if ( List$$1 ) { ArrayList.__proto__ = List$$1; }
96784           ArrayList.prototype = Object.create( List$$1 && List$$1.prototype );
96785           ArrayList.prototype.constructor = ArrayList;
96786
96787           ArrayList.prototype.ensureCapacity = function ensureCapacity () {};
96788           ArrayList.prototype.interfaces_ = function interfaces_ () { return [List$$1, Collection] };
96789
96790           /**
96791            * @override
96792            */
96793           ArrayList.prototype.add = function add (e) {
96794             if (arguments.length === 1) {
96795               this.array_.push(e);
96796             } else {
96797               this.array_.splice(arguments[0], arguments[1]);
96798             }
96799             return true
96800           };
96801
96802           ArrayList.prototype.clear = function clear () {
96803             this.array_ = [];
96804           };
96805
96806           /**
96807            * @override
96808            */
96809           ArrayList.prototype.addAll = function addAll (c) {
96810             var this$1 = this;
96811
96812             for (var i = c.iterator(); i.hasNext();) {
96813               this$1.add(i.next());
96814             }
96815             return true
96816           };
96817
96818           /**
96819            * @override
96820            */
96821           ArrayList.prototype.set = function set (index, element) {
96822             var oldElement = this.array_[index];
96823             this.array_[index] = element;
96824             return oldElement
96825           };
96826
96827           /**
96828            * @override
96829            */
96830           ArrayList.prototype.iterator = function iterator () {
96831             return new Iterator_(this)
96832           };
96833
96834           /**
96835            * @override
96836            */
96837           ArrayList.prototype.get = function get (index) {
96838             if (index < 0 || index >= this.size()) {
96839               throw new IndexOutOfBoundsException()
96840             }
96841
96842             return this.array_[index]
96843           };
96844
96845           /**
96846            * @override
96847            */
96848           ArrayList.prototype.isEmpty = function isEmpty () {
96849             return this.array_.length === 0
96850           };
96851
96852           /**
96853            * @override
96854            */
96855           ArrayList.prototype.size = function size () {
96856             return this.array_.length
96857           };
96858
96859           /**
96860            * @override
96861            */
96862           ArrayList.prototype.toArray = function toArray () {
96863             var this$1 = this;
96864
96865             var array = [];
96866
96867             for (var i = 0, len = this.array_.length; i < len; i++) {
96868               array.push(this$1.array_[i]);
96869             }
96870
96871             return array
96872           };
96873
96874           /**
96875            * @override
96876            */
96877           ArrayList.prototype.remove = function remove (o) {
96878             var this$1 = this;
96879
96880             var found = false;
96881
96882             for (var i = 0, len = this.array_.length; i < len; i++) {
96883               if (this$1.array_[i] === o) {
96884                 this$1.array_.splice(i, 1);
96885                 found = true;
96886                 break
96887               }
96888             }
96889
96890             return found
96891           };
96892
96893           return ArrayList;
96894         }(List));
96895
96896         /**
96897          * @extends {Iterator}
96898          * @param {ArrayList} arrayList
96899          * @constructor
96900          * @private
96901          */
96902         var Iterator_ = (function (Iterator$$1) {
96903           function Iterator_ (arrayList) {
96904             Iterator$$1.call(this);
96905             /**
96906              * @type {ArrayList}
96907              * @private
96908             */
96909             this.arrayList_ = arrayList;
96910             /**
96911              * @type {number}
96912              * @private
96913             */
96914             this.position_ = 0;
96915           }
96916
96917           if ( Iterator$$1 ) { Iterator_.__proto__ = Iterator$$1; }
96918           Iterator_.prototype = Object.create( Iterator$$1 && Iterator$$1.prototype );
96919           Iterator_.prototype.constructor = Iterator_;
96920
96921           /**
96922            * @override
96923            */
96924           Iterator_.prototype.next = function next () {
96925             if (this.position_ === this.arrayList_.size()) {
96926               throw new NoSuchElementException()
96927             }
96928             return this.arrayList_.get(this.position_++)
96929           };
96930
96931           /**
96932            * @override
96933            */
96934           Iterator_.prototype.hasNext = function hasNext () {
96935             if (this.position_ < this.arrayList_.size()) {
96936               return true
96937             } else {
96938               return false
96939             }
96940           };
96941
96942           /**
96943            * TODO: should be in ListIterator
96944            * @override
96945            */
96946           Iterator_.prototype.set = function set (element) {
96947             return this.arrayList_.set(this.position_ - 1, element)
96948           };
96949
96950           /**
96951            * @override
96952            */
96953           Iterator_.prototype.remove = function remove () {
96954             this.arrayList_.remove(this.arrayList_.get(this.position_));
96955           };
96956
96957           return Iterator_;
96958         }(Iterator$1));
96959
96960         var CoordinateList = (function (ArrayList$$1) {
96961           function CoordinateList () {
96962             ArrayList$$1.call(this);
96963             if (arguments.length === 0) ; else if (arguments.length === 1) {
96964               var coord = arguments[0];
96965               this.ensureCapacity(coord.length);
96966               this.add(coord, true);
96967             } else if (arguments.length === 2) {
96968               var coord$1 = arguments[0];
96969               var allowRepeated = arguments[1];
96970               this.ensureCapacity(coord$1.length);
96971               this.add(coord$1, allowRepeated);
96972             }
96973           }
96974
96975           if ( ArrayList$$1 ) { CoordinateList.__proto__ = ArrayList$$1; }
96976           CoordinateList.prototype = Object.create( ArrayList$$1 && ArrayList$$1.prototype );
96977           CoordinateList.prototype.constructor = CoordinateList;
96978
96979           var staticAccessors = { coordArrayType: { configurable: true } };
96980           staticAccessors.coordArrayType.get = function () { return new Array(0).fill(null) };
96981           CoordinateList.prototype.getCoordinate = function getCoordinate (i) {
96982             return this.get(i)
96983           };
96984           CoordinateList.prototype.addAll = function addAll () {
96985             var this$1 = this;
96986
96987             if (arguments.length === 2) {
96988               var coll = arguments[0];
96989               var allowRepeated = arguments[1];
96990               var isChanged = false;
96991               for (var i = coll.iterator(); i.hasNext();) {
96992                 this$1.add(i.next(), allowRepeated);
96993                 isChanged = true;
96994               }
96995               return isChanged
96996             } else { return ArrayList$$1.prototype.addAll.apply(this, arguments) }
96997           };
96998           CoordinateList.prototype.clone = function clone () {
96999             var this$1 = this;
97000
97001             var clone = ArrayList$$1.prototype.clone.call(this);
97002             for (var i = 0; i < this.size(); i++) {
97003               clone.add(i, this$1.get(i).copy());
97004             }
97005             return clone
97006           };
97007           CoordinateList.prototype.toCoordinateArray = function toCoordinateArray () {
97008             return this.toArray(CoordinateList.coordArrayType)
97009           };
97010           CoordinateList.prototype.add = function add () {
97011             var this$1 = this;
97012
97013             if (arguments.length === 1) {
97014               var coord = arguments[0];
97015               ArrayList$$1.prototype.add.call(this, coord);
97016             } else if (arguments.length === 2) {
97017               if (arguments[0] instanceof Array && typeof arguments[1] === 'boolean') {
97018                 var coord$1 = arguments[0];
97019                 var allowRepeated = arguments[1];
97020                 this.add(coord$1, allowRepeated, true);
97021                 return true
97022               } else if (arguments[0] instanceof Coordinate && typeof arguments[1] === 'boolean') {
97023                 var coord$2 = arguments[0];
97024                 var allowRepeated$1 = arguments[1];
97025                 if (!allowRepeated$1) {
97026                   if (this.size() >= 1) {
97027                     var last = this.get(this.size() - 1);
97028                     if (last.equals2D(coord$2)) { return null }
97029                   }
97030                 }
97031                 ArrayList$$1.prototype.add.call(this, coord$2);
97032               } else if (arguments[0] instanceof Object && typeof arguments[1] === 'boolean') {
97033                 var obj = arguments[0];
97034                 var allowRepeated$2 = arguments[1];
97035                 this.add(obj, allowRepeated$2);
97036                 return true
97037               }
97038             } else if (arguments.length === 3) {
97039               if (typeof arguments[2] === 'boolean' && (arguments[0] instanceof Array && typeof arguments[1] === 'boolean')) {
97040                 var coord$3 = arguments[0];
97041                 var allowRepeated$3 = arguments[1];
97042                 var direction = arguments[2];
97043                 if (direction) {
97044                   for (var i$1 = 0; i$1 < coord$3.length; i$1++) {
97045                     this$1.add(coord$3[i$1], allowRepeated$3);
97046                   }
97047                 } else {
97048                   for (var i$2 = coord$3.length - 1; i$2 >= 0; i$2--) {
97049                     this$1.add(coord$3[i$2], allowRepeated$3);
97050                   }
97051                 }
97052                 return true
97053               } else if (typeof arguments[2] === 'boolean' && (Number.isInteger(arguments[0]) && arguments[1] instanceof Coordinate)) {
97054                 var i$3 = arguments[0];
97055                 var coord$4 = arguments[1];
97056                 var allowRepeated$4 = arguments[2];
97057                 if (!allowRepeated$4) {
97058                   var size = this.size();
97059                   if (size > 0) {
97060                     if (i$3 > 0) {
97061                       var prev = this.get(i$3 - 1);
97062                       if (prev.equals2D(coord$4)) { return null }
97063                     }
97064                     if (i$3 < size) {
97065                       var next = this.get(i$3);
97066                       if (next.equals2D(coord$4)) { return null }
97067                     }
97068                   }
97069                 }
97070                 ArrayList$$1.prototype.add.call(this, i$3, coord$4);
97071               }
97072             } else if (arguments.length === 4) {
97073               var coord$5 = arguments[0];
97074               var allowRepeated$5 = arguments[1];
97075               var start = arguments[2];
97076               var end = arguments[3];
97077               var inc = 1;
97078               if (start > end) { inc = -1; }
97079               for (var i = start; i !== end; i += inc) {
97080                 this$1.add(coord$5[i], allowRepeated$5);
97081               }
97082               return true
97083             }
97084           };
97085           CoordinateList.prototype.closeRing = function closeRing () {
97086             if (this.size() > 0) { this.add(new Coordinate(this.get(0)), false); }
97087           };
97088           CoordinateList.prototype.interfaces_ = function interfaces_ () {
97089             return []
97090           };
97091           CoordinateList.prototype.getClass = function getClass () {
97092             return CoordinateList
97093           };
97094
97095           Object.defineProperties( CoordinateList, staticAccessors );
97096
97097           return CoordinateList;
97098         }(ArrayList));
97099
97100         var CoordinateArrays = function CoordinateArrays () {};
97101
97102         var staticAccessors$13 = { ForwardComparator: { configurable: true },BidirectionalComparator: { configurable: true },coordArrayType: { configurable: true } };
97103
97104         staticAccessors$13.ForwardComparator.get = function () { return ForwardComparator };
97105         staticAccessors$13.BidirectionalComparator.get = function () { return BidirectionalComparator };
97106         staticAccessors$13.coordArrayType.get = function () { return new Array(0).fill(null) };
97107
97108         CoordinateArrays.prototype.interfaces_ = function interfaces_ () {
97109           return []
97110         };
97111         CoordinateArrays.prototype.getClass = function getClass () {
97112           return CoordinateArrays
97113         };
97114         CoordinateArrays.isRing = function isRing (pts) {
97115           if (pts.length < 4) { return false }
97116           if (!pts[0].equals2D(pts[pts.length - 1])) { return false }
97117           return true
97118         };
97119         CoordinateArrays.ptNotInList = function ptNotInList (testPts, pts) {
97120           for (var i = 0; i < testPts.length; i++) {
97121             var testPt = testPts[i];
97122             if (CoordinateArrays.indexOf(testPt, pts) < 0) { return testPt }
97123           }
97124           return null
97125         };
97126         CoordinateArrays.scroll = function scroll (coordinates, firstCoordinate) {
97127           var i = CoordinateArrays.indexOf(firstCoordinate, coordinates);
97128           if (i < 0) { return null }
97129           var newCoordinates = new Array(coordinates.length).fill(null);
97130           System.arraycopy(coordinates, i, newCoordinates, 0, coordinates.length - i);
97131           System.arraycopy(coordinates, 0, newCoordinates, coordinates.length - i, i);
97132           System.arraycopy(newCoordinates, 0, coordinates, 0, coordinates.length);
97133         };
97134         CoordinateArrays.equals = function equals () {
97135           if (arguments.length === 2) {
97136             var coord1 = arguments[0];
97137             var coord2 = arguments[1];
97138             if (coord1 === coord2) { return true }
97139             if (coord1 === null || coord2 === null) { return false }
97140             if (coord1.length !== coord2.length) { return false }
97141             for (var i = 0; i < coord1.length; i++) {
97142               if (!coord1[i].equals(coord2[i])) { return false }
97143             }
97144             return true
97145           } else if (arguments.length === 3) {
97146             var coord1$1 = arguments[0];
97147             var coord2$1 = arguments[1];
97148             var coordinateComparator = arguments[2];
97149             if (coord1$1 === coord2$1) { return true }
97150             if (coord1$1 === null || coord2$1 === null) { return false }
97151             if (coord1$1.length !== coord2$1.length) { return false }
97152             for (var i$1 = 0; i$1 < coord1$1.length; i$1++) {
97153               if (coordinateComparator.compare(coord1$1[i$1], coord2$1[i$1]) !== 0) { return false }
97154             }
97155             return true
97156           }
97157         };
97158         CoordinateArrays.intersection = function intersection (coordinates, env) {
97159           var coordList = new CoordinateList();
97160           for (var i = 0; i < coordinates.length; i++) {
97161             if (env.intersects(coordinates[i])) { coordList.add(coordinates[i], true); }
97162           }
97163           return coordList.toCoordinateArray()
97164         };
97165         CoordinateArrays.hasRepeatedPoints = function hasRepeatedPoints (coord) {
97166           for (var i = 1; i < coord.length; i++) {
97167             if (coord[i - 1].equals(coord[i])) {
97168               return true
97169             }
97170           }
97171           return false
97172         };
97173         CoordinateArrays.removeRepeatedPoints = function removeRepeatedPoints (coord) {
97174           if (!CoordinateArrays.hasRepeatedPoints(coord)) { return coord }
97175           var coordList = new CoordinateList(coord, false);
97176           return coordList.toCoordinateArray()
97177         };
97178         CoordinateArrays.reverse = function reverse (coord) {
97179           var last = coord.length - 1;
97180           var mid = Math.trunc(last / 2);
97181           for (var i = 0; i <= mid; i++) {
97182             var tmp = coord[i];
97183             coord[i] = coord[last - i];
97184             coord[last - i] = tmp;
97185           }
97186         };
97187         CoordinateArrays.removeNull = function removeNull (coord) {
97188           var nonNull = 0;
97189           for (var i = 0; i < coord.length; i++) {
97190             if (coord[i] !== null) { nonNull++; }
97191           }
97192           var newCoord = new Array(nonNull).fill(null);
97193           if (nonNull === 0) { return newCoord }
97194           var j = 0;
97195           for (var i$1 = 0; i$1 < coord.length; i$1++) {
97196             if (coord[i$1] !== null) { newCoord[j++] = coord[i$1]; }
97197           }
97198           return newCoord
97199         };
97200         CoordinateArrays.copyDeep = function copyDeep () {
97201           if (arguments.length === 1) {
97202             var coordinates = arguments[0];
97203             var copy = new Array(coordinates.length).fill(null);
97204             for (var i = 0; i < coordinates.length; i++) {
97205               copy[i] = new Coordinate(coordinates[i]);
97206             }
97207             return copy
97208           } else if (arguments.length === 5) {
97209             var src = arguments[0];
97210             var srcStart = arguments[1];
97211             var dest = arguments[2];
97212             var destStart = arguments[3];
97213             var length = arguments[4];
97214             for (var i$1 = 0; i$1 < length; i$1++) {
97215               dest[destStart + i$1] = new Coordinate(src[srcStart + i$1]);
97216             }
97217           }
97218         };
97219         CoordinateArrays.isEqualReversed = function isEqualReversed (pts1, pts2) {
97220           for (var i = 0; i < pts1.length; i++) {
97221             var p1 = pts1[i];
97222             var p2 = pts2[pts1.length - i - 1];
97223             if (p1.compareTo(p2) !== 0) { return false }
97224           }
97225           return true
97226         };
97227         CoordinateArrays.envelope = function envelope (coordinates) {
97228           var env = new Envelope();
97229           for (var i = 0; i < coordinates.length; i++) {
97230             env.expandToInclude(coordinates[i]);
97231           }
97232           return env
97233         };
97234         CoordinateArrays.toCoordinateArray = function toCoordinateArray (coordList) {
97235           return coordList.toArray(CoordinateArrays.coordArrayType)
97236         };
97237         CoordinateArrays.atLeastNCoordinatesOrNothing = function atLeastNCoordinatesOrNothing (n, c) {
97238           return c.length >= n ? c : []
97239         };
97240         CoordinateArrays.indexOf = function indexOf (coordinate, coordinates) {
97241           for (var i = 0; i < coordinates.length; i++) {
97242             if (coordinate.equals(coordinates[i])) {
97243               return i
97244             }
97245           }
97246           return -1
97247         };
97248         CoordinateArrays.increasingDirection = function increasingDirection (pts) {
97249           for (var i = 0; i < Math.trunc(pts.length / 2); i++) {
97250             var j = pts.length - 1 - i;
97251             var comp = pts[i].compareTo(pts[j]);
97252             if (comp !== 0) { return comp }
97253           }
97254           return 1
97255         };
97256         CoordinateArrays.compare = function compare (pts1, pts2) {
97257           var i = 0;
97258           while (i < pts1.length && i < pts2.length) {
97259             var compare = pts1[i].compareTo(pts2[i]);
97260             if (compare !== 0) { return compare }
97261             i++;
97262           }
97263           if (i < pts2.length) { return -1 }
97264           if (i < pts1.length) { return 1 }
97265           return 0
97266         };
97267         CoordinateArrays.minCoordinate = function minCoordinate (coordinates) {
97268           var minCoord = null;
97269           for (var i = 0; i < coordinates.length; i++) {
97270             if (minCoord === null || minCoord.compareTo(coordinates[i]) > 0) {
97271               minCoord = coordinates[i];
97272             }
97273           }
97274           return minCoord
97275         };
97276         CoordinateArrays.extract = function extract (pts, start, end) {
97277           start = MathUtil.clamp(start, 0, pts.length);
97278           end = MathUtil.clamp(end, -1, pts.length);
97279           var npts = end - start + 1;
97280           if (end < 0) { npts = 0; }
97281           if (start >= pts.length) { npts = 0; }
97282           if (end < start) { npts = 0; }
97283           var extractPts = new Array(npts).fill(null);
97284           if (npts === 0) { return extractPts }
97285           var iPts = 0;
97286           for (var i = start; i <= end; i++) {
97287             extractPts[iPts++] = pts[i];
97288           }
97289           return extractPts
97290         };
97291
97292         Object.defineProperties( CoordinateArrays, staticAccessors$13 );
97293
97294         var ForwardComparator = function ForwardComparator () {};
97295
97296         ForwardComparator.prototype.compare = function compare (o1, o2) {
97297           var pts1 = o1;
97298           var pts2 = o2;
97299           return CoordinateArrays.compare(pts1, pts2)
97300         };
97301         ForwardComparator.prototype.interfaces_ = function interfaces_ () {
97302           return [Comparator]
97303         };
97304         ForwardComparator.prototype.getClass = function getClass () {
97305           return ForwardComparator
97306         };
97307
97308         var BidirectionalComparator = function BidirectionalComparator () {};
97309
97310         BidirectionalComparator.prototype.compare = function compare (o1, o2) {
97311           var pts1 = o1;
97312           var pts2 = o2;
97313           if (pts1.length < pts2.length) { return -1 }
97314           if (pts1.length > pts2.length) { return 1 }
97315           if (pts1.length === 0) { return 0 }
97316           var forwardComp = CoordinateArrays.compare(pts1, pts2);
97317           var isEqualRev = CoordinateArrays.isEqualReversed(pts1, pts2);
97318           if (isEqualRev) { return 0 }
97319           return forwardComp
97320         };
97321         BidirectionalComparator.prototype.OLDcompare = function OLDcompare (o1, o2) {
97322           var pts1 = o1;
97323           var pts2 = o2;
97324           if (pts1.length < pts2.length) { return -1 }
97325           if (pts1.length > pts2.length) { return 1 }
97326           if (pts1.length === 0) { return 0 }
97327           var dir1 = CoordinateArrays.increasingDirection(pts1);
97328           var dir2 = CoordinateArrays.increasingDirection(pts2);
97329           var i1 = dir1 > 0 ? 0 : pts1.length - 1;
97330           var i2 = dir2 > 0 ? 0 : pts1.length - 1;
97331           for (var i = 0; i < pts1.length; i++) {
97332             var comparePt = pts1[i1].compareTo(pts2[i2]);
97333             if (comparePt !== 0) { return comparePt }
97334             i1 += dir1;
97335             i2 += dir2;
97336           }
97337           return 0
97338         };
97339         BidirectionalComparator.prototype.interfaces_ = function interfaces_ () {
97340           return [Comparator]
97341         };
97342         BidirectionalComparator.prototype.getClass = function getClass () {
97343           return BidirectionalComparator
97344         };
97345
97346         /**
97347          * @see http://download.oracle.com/javase/6/docs/api/java/util/Map.html
97348          *
97349          * @constructor
97350          * @private
97351          */
97352         var Map$1$1 = function Map () {};
97353
97354         Map$1$1.prototype.get = function get () {};
97355         /**
97356          * Associates the specified value with the specified key in this map (optional
97357          * operation).
97358          * @param {Object} key
97359          * @param {Object} value
97360          * @return {Object}
97361          */
97362         Map$1$1.prototype.put = function put () {};
97363
97364         /**
97365          * Returns the number of key-value mappings in this map.
97366          * @return {number}
97367          */
97368         Map$1$1.prototype.size = function size () {};
97369
97370         /**
97371          * Returns a Collection view of the values contained in this map.
97372          * @return {javascript.util.Collection}
97373          */
97374         Map$1$1.prototype.values = function values () {};
97375
97376         /**
97377          * Returns a {@link Set} view of the mappings contained in this map.
97378          * The set is backed by the map, so changes to the map are
97379          * reflected in the set, and vice-versa.If the map is modified
97380          * while an iteration over the set is in progress (except through
97381          * the iterator's own <tt>remove</tt> operation, or through the
97382          * <tt>setValue</tt> operation on a map entry returned by the
97383          * iterator) the results of the iteration are undefined.The set
97384          * supports element removal, which removes the corresponding
97385          * mapping from the map, via the <tt>Iterator.remove</tt>,
97386          * <tt>Set.remove</tt>, <tt>removeAll</tt>, <tt>retainAll</tt> and
97387          * <tt>clear</tt> operations.It does not support the
97388          * <tt>add</tt> or <tt>addAll</tt> operations.
97389          *
97390          * @return {Set} a set view of the mappings contained in this map
97391          */
97392         Map$1$1.prototype.entrySet = function entrySet () {};
97393
97394         /**
97395          * @see http://download.oracle.com/javase/6/docs/api/java/util/SortedMap.html
97396          *
97397          * @extends {Map}
97398          * @constructor
97399          * @private
97400          */
97401         var SortedMap = (function (Map) {
97402                 function SortedMap () {
97403                         Map.apply(this, arguments);
97404                 }if ( Map ) { SortedMap.__proto__ = Map; }
97405                 SortedMap.prototype = Object.create( Map && Map.prototype );
97406                 SortedMap.prototype.constructor = SortedMap;
97407
97408                 
97409
97410                 return SortedMap;
97411         }(Map$1$1));
97412
97413         /**
97414          * @param {string=} message Optional message
97415          * @extends {Error}
97416          * @constructor
97417          * @private
97418          */
97419         function OperationNotSupported (message) {
97420           this.message = message || '';
97421         }
97422         OperationNotSupported.prototype = new Error();
97423
97424         /**
97425          * @type {string}
97426          */
97427         OperationNotSupported.prototype.name = 'OperationNotSupported';
97428
97429         /**
97430          * @see http://download.oracle.com/javase/6/docs/api/java/util/Set.html
97431          *
97432          * @extends {Collection}
97433          * @constructor
97434          * @private
97435          */
97436         function Set$2() {}
97437         Set$2.prototype = new Collection();
97438
97439
97440         /**
97441          * Returns true if this set contains the specified element. More formally,
97442          * returns true if and only if this set contains an element e such that (o==null ?
97443          * e==null : o.equals(e)).
97444          * @param {Object} e
97445          * @return {boolean}
97446          */
97447         Set$2.prototype.contains = function() {};
97448
97449         /**
97450          * @see http://docs.oracle.com/javase/6/docs/api/java/util/HashSet.html
97451          *
97452          * @extends {javascript.util.Set}
97453          * @constructor
97454          * @private
97455          */
97456         var HashSet = (function (Set$$1) {
97457           function HashSet () {
97458             Set$$1.call(this);
97459             this.array_ = [];
97460
97461             if (arguments[0] instanceof Collection) {
97462               this.addAll(arguments[0]);
97463             }
97464           }
97465
97466           if ( Set$$1 ) { HashSet.__proto__ = Set$$1; }
97467           HashSet.prototype = Object.create( Set$$1 && Set$$1.prototype );
97468           HashSet.prototype.constructor = HashSet;
97469
97470           /**
97471            * @override
97472            */
97473           HashSet.prototype.contains = function contains (o) {
97474             var this$1 = this;
97475
97476             for (var i = 0, len = this.array_.length; i < len; i++) {
97477               var e = this$1.array_[i];
97478               if (e === o) {
97479                 return true
97480               }
97481             }
97482             return false
97483           };
97484
97485           /**
97486            * @override
97487            */
97488           HashSet.prototype.add = function add (o) {
97489             if (this.contains(o)) {
97490               return false
97491             }
97492
97493             this.array_.push(o);
97494
97495             return true
97496           };
97497
97498           /**
97499            * @override
97500            */
97501           HashSet.prototype.addAll = function addAll (c) {
97502             var this$1 = this;
97503
97504             for (var i = c.iterator(); i.hasNext();) {
97505               this$1.add(i.next());
97506             }
97507             return true
97508           };
97509
97510           /**
97511            * @override
97512            */
97513           HashSet.prototype.remove = function remove (o) {
97514             // throw new javascript.util.OperationNotSupported()
97515             throw new Error()
97516           };
97517
97518           /**
97519            * @override
97520            */
97521           HashSet.prototype.size = function size () {
97522             return this.array_.length
97523           };
97524
97525           /**
97526            * @override
97527            */
97528           HashSet.prototype.isEmpty = function isEmpty () {
97529             return this.array_.length === 0
97530           };
97531
97532           /**
97533            * @override
97534            */
97535           HashSet.prototype.toArray = function toArray () {
97536             var this$1 = this;
97537
97538             var array = [];
97539
97540             for (var i = 0, len = this.array_.length; i < len; i++) {
97541               array.push(this$1.array_[i]);
97542             }
97543
97544             return array
97545           };
97546
97547           /**
97548            * @override
97549            */
97550           HashSet.prototype.iterator = function iterator () {
97551             return new Iterator_$1(this)
97552           };
97553
97554           return HashSet;
97555         }(Set$2));
97556
97557         /**
97558            * @extends {Iterator}
97559            * @param {HashSet} hashSet
97560            * @constructor
97561            * @private
97562            */
97563         var Iterator_$1 = (function (Iterator$$1) {
97564           function Iterator_ (hashSet) {
97565             Iterator$$1.call(this);
97566             /**
97567              * @type {HashSet}
97568              * @private
97569              */
97570             this.hashSet_ = hashSet;
97571             /**
97572              * @type {number}
97573              * @private
97574              */
97575             this.position_ = 0;
97576           }
97577
97578           if ( Iterator$$1 ) { Iterator_.__proto__ = Iterator$$1; }
97579           Iterator_.prototype = Object.create( Iterator$$1 && Iterator$$1.prototype );
97580           Iterator_.prototype.constructor = Iterator_;
97581
97582           /**
97583            * @override
97584            */
97585           Iterator_.prototype.next = function next () {
97586             if (this.position_ === this.hashSet_.size()) {
97587               throw new NoSuchElementException()
97588             }
97589             return this.hashSet_.array_[this.position_++]
97590           };
97591
97592           /**
97593            * @override
97594            */
97595           Iterator_.prototype.hasNext = function hasNext () {
97596             if (this.position_ < this.hashSet_.size()) {
97597               return true
97598             } else {
97599               return false
97600             }
97601           };
97602
97603           /**
97604            * @override
97605            */
97606           Iterator_.prototype.remove = function remove () {
97607             throw new OperationNotSupported()
97608           };
97609
97610           return Iterator_;
97611         }(Iterator$1));
97612
97613         var BLACK = 0;
97614         var RED = 1;
97615         function colorOf (p) { return (p === null ? BLACK : p.color) }
97616         function parentOf (p) { return (p === null ? null : p.parent) }
97617         function setColor (p, c) { if (p !== null) { p.color = c; } }
97618         function leftOf (p) { return (p === null ? null : p.left) }
97619         function rightOf (p) { return (p === null ? null : p.right) }
97620
97621         /**
97622          * @see http://download.oracle.com/javase/6/docs/api/java/util/TreeMap.html
97623          *
97624          * @extends {SortedMap}
97625          * @constructor
97626          * @private
97627          */
97628         function TreeMap () {
97629           /**
97630            * @type {Object}
97631            * @private
97632            */
97633           this.root_ = null;
97634           /**
97635            * @type {number}
97636            * @private
97637           */
97638           this.size_ = 0;
97639         }
97640         TreeMap.prototype = new SortedMap();
97641
97642         /**
97643          * @override
97644          */
97645         TreeMap.prototype.get = function (key) {
97646           var p = this.root_;
97647           while (p !== null) {
97648             var cmp = key['compareTo'](p.key);
97649             if (cmp < 0) { p = p.left; }
97650             else if (cmp > 0) { p = p.right; }
97651             else { return p.value }
97652           }
97653           return null
97654         };
97655
97656         /**
97657          * @override
97658          */
97659         TreeMap.prototype.put = function (key, value) {
97660           if (this.root_ === null) {
97661             this.root_ = {
97662               key: key,
97663               value: value,
97664               left: null,
97665               right: null,
97666               parent: null,
97667               color: BLACK,
97668               getValue: function getValue () { return this.value },
97669               getKey: function getKey () { return this.key }
97670             };
97671             this.size_ = 1;
97672             return null
97673           }
97674           var t = this.root_;
97675           var parent;
97676           var cmp;
97677           do {
97678             parent = t;
97679             cmp = key['compareTo'](t.key);
97680             if (cmp < 0) {
97681               t = t.left;
97682             } else if (cmp > 0) {
97683               t = t.right;
97684             } else {
97685               var oldValue = t.value;
97686               t.value = value;
97687               return oldValue
97688             }
97689           } while (t !== null)
97690           var e = {
97691             key: key,
97692             left: null,
97693             right: null,
97694             value: value,
97695             parent: parent,
97696             color: BLACK,
97697             getValue: function getValue () { return this.value },
97698             getKey: function getKey () { return this.key }
97699           };
97700           if (cmp < 0) {
97701             parent.left = e;
97702           } else {
97703             parent.right = e;
97704           }
97705           this.fixAfterInsertion(e);
97706           this.size_++;
97707           return null
97708         };
97709
97710         /**
97711          * @param {Object} x
97712          */
97713         TreeMap.prototype.fixAfterInsertion = function (x) {
97714           var this$1 = this;
97715
97716           x.color = RED;
97717           while (x != null && x !== this.root_ && x.parent.color === RED) {
97718             if (parentOf(x) === leftOf(parentOf(parentOf(x)))) {
97719               var y = rightOf(parentOf(parentOf(x)));
97720               if (colorOf(y) === RED) {
97721                 setColor(parentOf(x), BLACK);
97722                 setColor(y, BLACK);
97723                 setColor(parentOf(parentOf(x)), RED);
97724                 x = parentOf(parentOf(x));
97725               } else {
97726                 if (x === rightOf(parentOf(x))) {
97727                   x = parentOf(x);
97728                   this$1.rotateLeft(x);
97729                 }
97730                 setColor(parentOf(x), BLACK);
97731                 setColor(parentOf(parentOf(x)), RED);
97732                 this$1.rotateRight(parentOf(parentOf(x)));
97733               }
97734             } else {
97735               var y$1 = leftOf(parentOf(parentOf(x)));
97736               if (colorOf(y$1) === RED) {
97737                 setColor(parentOf(x), BLACK);
97738                 setColor(y$1, BLACK);
97739                 setColor(parentOf(parentOf(x)), RED);
97740                 x = parentOf(parentOf(x));
97741               } else {
97742                 if (x === leftOf(parentOf(x))) {
97743                   x = parentOf(x);
97744                   this$1.rotateRight(x);
97745                 }
97746                 setColor(parentOf(x), BLACK);
97747                 setColor(parentOf(parentOf(x)), RED);
97748                 this$1.rotateLeft(parentOf(parentOf(x)));
97749               }
97750             }
97751           }
97752           this.root_.color = BLACK;
97753         };
97754
97755         /**
97756          * @override
97757          */
97758         TreeMap.prototype.values = function () {
97759           var arrayList = new ArrayList();
97760           var p = this.getFirstEntry();
97761           if (p !== null) {
97762             arrayList.add(p.value);
97763             while ((p = TreeMap.successor(p)) !== null) {
97764               arrayList.add(p.value);
97765             }
97766           }
97767           return arrayList
97768         };
97769
97770         /**
97771          * @override
97772          */
97773         TreeMap.prototype.entrySet = function () {
97774           var hashSet = new HashSet();
97775           var p = this.getFirstEntry();
97776           if (p !== null) {
97777             hashSet.add(p);
97778             while ((p = TreeMap.successor(p)) !== null) {
97779               hashSet.add(p);
97780             }
97781           }
97782           return hashSet
97783         };
97784
97785         /**
97786          * @param {Object} p
97787          */
97788         TreeMap.prototype.rotateLeft = function (p) {
97789           if (p != null) {
97790             var r = p.right;
97791             p.right = r.left;
97792             if (r.left != null) { r.left.parent = p; }
97793             r.parent = p.parent;
97794             if (p.parent === null) { this.root_ = r; } else if (p.parent.left === p) { p.parent.left = r; } else { p.parent.right = r; }
97795             r.left = p;
97796             p.parent = r;
97797           }
97798         };
97799
97800         /**
97801          * @param {Object} p
97802          */
97803         TreeMap.prototype.rotateRight = function (p) {
97804           if (p != null) {
97805             var l = p.left;
97806             p.left = l.right;
97807             if (l.right != null) { l.right.parent = p; }
97808             l.parent = p.parent;
97809             if (p.parent === null) { this.root_ = l; } else if (p.parent.right === p) { p.parent.right = l; } else { p.parent.left = l; }
97810             l.right = p;
97811             p.parent = l;
97812           }
97813         };
97814
97815         /**
97816          * @return {Object}
97817          */
97818         TreeMap.prototype.getFirstEntry = function () {
97819           var p = this.root_;
97820           if (p != null) {
97821             while (p.left != null) {
97822               p = p.left;
97823             }
97824           }
97825           return p
97826         };
97827
97828         /**
97829          * @param {Object} t
97830          * @return {Object}
97831          * @private
97832          */
97833         TreeMap.successor = function (t) {
97834           if (t === null) { return null } else if (t.right !== null) {
97835             var p = t.right;
97836             while (p.left !== null) {
97837               p = p.left;
97838             }
97839             return p
97840           } else {
97841             var p$1 = t.parent;
97842             var ch = t;
97843             while (p$1 !== null && ch === p$1.right) {
97844               ch = p$1;
97845               p$1 = p$1.parent;
97846             }
97847             return p$1
97848           }
97849         };
97850
97851         /**
97852          * @override
97853          */
97854         TreeMap.prototype.size = function () {
97855           return this.size_
97856         };
97857
97858         var Lineal = function Lineal () {};
97859
97860         Lineal.prototype.interfaces_ = function interfaces_ () {
97861           return []
97862         };
97863         Lineal.prototype.getClass = function getClass () {
97864           return Lineal
97865         };
97866
97867         /**
97868          * @see http://download.oracle.com/javase/6/docs/api/java/util/SortedSet.html
97869          *
97870          * @extends {Set}
97871          * @constructor
97872          * @private
97873          */
97874         function SortedSet () {}
97875         SortedSet.prototype = new Set$2();
97876
97877         // import Iterator from './Iterator'
97878         /**
97879          * @see http://download.oracle.com/javase/6/docs/api/java/util/TreeSet.html
97880          *
97881          * @extends {SortedSet}
97882          * @constructor
97883          * @private
97884          */
97885         function TreeSet () {
97886           /**
97887            * @type {Array}
97888            * @private
97889           */
97890           this.array_ = [];
97891
97892           if (arguments[0] instanceof Collection) {
97893             this.addAll(arguments[0]);
97894           }
97895         }
97896         TreeSet.prototype = new SortedSet();
97897
97898         /**
97899          * @override
97900          */
97901         TreeSet.prototype.contains = function (o) {
97902           var this$1 = this;
97903
97904           for (var i = 0, len = this.array_.length; i < len; i++) {
97905             var e = this$1.array_[i];
97906             if (e['compareTo'](o) === 0) {
97907               return true
97908             }
97909           }
97910           return false
97911         };
97912
97913         /**
97914          * @override
97915          */
97916         TreeSet.prototype.add = function (o) {
97917           var this$1 = this;
97918
97919           if (this.contains(o)) {
97920             return false
97921           }
97922
97923           for (var i = 0, len = this.array_.length; i < len; i++) {
97924             var e = this$1.array_[i];
97925             if (e['compareTo'](o) === 1) {
97926               this$1.array_.splice(i, 0, o);
97927               return true
97928             }
97929           }
97930
97931           this.array_.push(o);
97932
97933           return true
97934         };
97935
97936         /**
97937          * @override
97938          */
97939         TreeSet.prototype.addAll = function (c) {
97940           var this$1 = this;
97941
97942           for (var i = c.iterator(); i.hasNext();) {
97943             this$1.add(i.next());
97944           }
97945           return true
97946         };
97947
97948         /**
97949          * @override
97950          */
97951         TreeSet.prototype.remove = function (e) {
97952           throw new OperationNotSupported()
97953         };
97954
97955         /**
97956          * @override
97957          */
97958         TreeSet.prototype.size = function () {
97959           return this.array_.length
97960         };
97961
97962         /**
97963          * @override
97964          */
97965         TreeSet.prototype.isEmpty = function () {
97966           return this.array_.length === 0
97967         };
97968
97969         /**
97970          * @override
97971          */
97972         TreeSet.prototype.toArray = function () {
97973           var this$1 = this;
97974
97975           var array = [];
97976
97977           for (var i = 0, len = this.array_.length; i < len; i++) {
97978             array.push(this$1.array_[i]);
97979           }
97980
97981           return array
97982         };
97983
97984         /**
97985          * @override
97986          */
97987         TreeSet.prototype.iterator = function () {
97988           return new Iterator_$2(this)
97989         };
97990
97991         /**
97992          * @extends {javascript.util.Iterator}
97993          * @param {javascript.util.TreeSet} treeSet
97994          * @constructor
97995          * @private
97996          */
97997         var Iterator_$2 = function (treeSet) {
97998           /**
97999            * @type {javascript.util.TreeSet}
98000            * @private
98001            */
98002           this.treeSet_ = treeSet;
98003           /**
98004            * @type {number}
98005            * @private
98006            */
98007           this.position_ = 0;
98008         };
98009
98010         /**
98011          * @override
98012          */
98013         Iterator_$2.prototype.next = function () {
98014           if (this.position_ === this.treeSet_.size()) {
98015             throw new NoSuchElementException()
98016           }
98017           return this.treeSet_.array_[this.position_++]
98018         };
98019
98020         /**
98021          * @override
98022          */
98023         Iterator_$2.prototype.hasNext = function () {
98024           if (this.position_ < this.treeSet_.size()) {
98025             return true
98026           } else {
98027             return false
98028           }
98029         };
98030
98031         /**
98032          * @override
98033          */
98034         Iterator_$2.prototype.remove = function () {
98035           throw new OperationNotSupported()
98036         };
98037
98038         /**
98039          * @see http://download.oracle.com/javase/6/docs/api/java/util/Arrays.html
98040          *
98041          * @constructor
98042          * @private
98043          */
98044         var Arrays = function Arrays () {};
98045
98046         Arrays.sort = function sort () {
98047           var a = arguments[0];
98048           var i;
98049           var t;
98050           var comparator;
98051           var compare;
98052           if (arguments.length === 1) {
98053             compare = function (a, b) {
98054               return a.compareTo(b)
98055             };
98056             a.sort(compare);
98057           } else if (arguments.length === 2) {
98058             comparator = arguments[1];
98059             compare = function (a, b) {
98060               return comparator['compare'](a, b)
98061             };
98062             a.sort(compare);
98063           } else if (arguments.length === 3) {
98064             t = a.slice(arguments[1], arguments[2]);
98065             t.sort();
98066             var r = a.slice(0, arguments[1]).concat(t, a.slice(arguments[2], a.length));
98067             a.splice(0, a.length);
98068             for (i = 0; i < r.length; i++) {
98069               a.push(r[i]);
98070             }
98071           } else if (arguments.length === 4) {
98072             t = a.slice(arguments[1], arguments[2]);
98073             comparator = arguments[3];
98074             compare = function (a, b) {
98075               return comparator['compare'](a, b)
98076             };
98077             t.sort(compare);
98078             r = a.slice(0, arguments[1]).concat(t, a.slice(arguments[2], a.length));
98079             a.splice(0, a.length);
98080             for (i = 0; i < r.length; i++) {
98081               a.push(r[i]);
98082             }
98083           }
98084         };
98085         /**
98086          * @param {Array} array
98087          * @return {ArrayList}
98088          */
98089         Arrays.asList = function asList (array) {
98090           var arrayList = new ArrayList();
98091           for (var i = 0, len = array.length; i < len; i++) {
98092             arrayList.add(array[i]);
98093           }
98094           return arrayList
98095         };
98096
98097         var Dimension = function Dimension () {};
98098
98099         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 } };
98100
98101         staticAccessors$14.P.get = function () { return 0 };
98102         staticAccessors$14.L.get = function () { return 1 };
98103         staticAccessors$14.A.get = function () { return 2 };
98104         staticAccessors$14.FALSE.get = function () { return -1 };
98105         staticAccessors$14.TRUE.get = function () { return -2 };
98106         staticAccessors$14.DONTCARE.get = function () { return -3 };
98107         staticAccessors$14.SYM_FALSE.get = function () { return 'F' };
98108         staticAccessors$14.SYM_TRUE.get = function () { return 'T' };
98109         staticAccessors$14.SYM_DONTCARE.get = function () { return '*' };
98110         staticAccessors$14.SYM_P.get = function () { return '0' };
98111         staticAccessors$14.SYM_L.get = function () { return '1' };
98112         staticAccessors$14.SYM_A.get = function () { return '2' };
98113
98114         Dimension.prototype.interfaces_ = function interfaces_ () {
98115           return []
98116         };
98117         Dimension.prototype.getClass = function getClass () {
98118           return Dimension
98119         };
98120         Dimension.toDimensionSymbol = function toDimensionSymbol (dimensionValue) {
98121           switch (dimensionValue) {
98122             case Dimension.FALSE:
98123               return Dimension.SYM_FALSE
98124             case Dimension.TRUE:
98125               return Dimension.SYM_TRUE
98126             case Dimension.DONTCARE:
98127               return Dimension.SYM_DONTCARE
98128             case Dimension.P:
98129               return Dimension.SYM_P
98130             case Dimension.L:
98131               return Dimension.SYM_L
98132             case Dimension.A:
98133               return Dimension.SYM_A
98134           }
98135           throw new IllegalArgumentException('Unknown dimension value: ' + dimensionValue)
98136         };
98137         Dimension.toDimensionValue = function toDimensionValue (dimensionSymbol) {
98138           switch (Character.toUpperCase(dimensionSymbol)) {
98139             case Dimension.SYM_FALSE:
98140               return Dimension.FALSE
98141             case Dimension.SYM_TRUE:
98142               return Dimension.TRUE
98143             case Dimension.SYM_DONTCARE:
98144               return Dimension.DONTCARE
98145             case Dimension.SYM_P:
98146               return Dimension.P
98147             case Dimension.SYM_L:
98148               return Dimension.L
98149             case Dimension.SYM_A:
98150               return Dimension.A
98151           }
98152           throw new IllegalArgumentException('Unknown dimension symbol: ' + dimensionSymbol)
98153         };
98154
98155         Object.defineProperties( Dimension, staticAccessors$14 );
98156
98157         var GeometryFilter = function GeometryFilter () {};
98158
98159         GeometryFilter.prototype.filter = function filter (geom) {};
98160         GeometryFilter.prototype.interfaces_ = function interfaces_ () {
98161           return []
98162         };
98163         GeometryFilter.prototype.getClass = function getClass () {
98164           return GeometryFilter
98165         };
98166
98167         var CoordinateSequenceFilter = function CoordinateSequenceFilter () {};
98168
98169         CoordinateSequenceFilter.prototype.filter = function filter (seq, i) {};
98170         CoordinateSequenceFilter.prototype.isDone = function isDone () {};
98171         CoordinateSequenceFilter.prototype.isGeometryChanged = function isGeometryChanged () {};
98172         CoordinateSequenceFilter.prototype.interfaces_ = function interfaces_ () {
98173           return []
98174         };
98175         CoordinateSequenceFilter.prototype.getClass = function getClass () {
98176           return CoordinateSequenceFilter
98177         };
98178
98179         var GeometryCollection = (function (Geometry$$1) {
98180           function GeometryCollection (geometries, factory) {
98181             Geometry$$1.call(this, factory);
98182             this._geometries = geometries || [];
98183
98184             if (Geometry$$1.hasNullElements(this._geometries)) {
98185               throw new IllegalArgumentException('geometries must not contain null elements')
98186             }
98187           }
98188
98189           if ( Geometry$$1 ) { GeometryCollection.__proto__ = Geometry$$1; }
98190           GeometryCollection.prototype = Object.create( Geometry$$1 && Geometry$$1.prototype );
98191           GeometryCollection.prototype.constructor = GeometryCollection;
98192
98193           var staticAccessors = { serialVersionUID: { configurable: true } };
98194           GeometryCollection.prototype.computeEnvelopeInternal = function computeEnvelopeInternal () {
98195             var this$1 = this;
98196
98197             var envelope = new Envelope();
98198             for (var i = 0; i < this._geometries.length; i++) {
98199               envelope.expandToInclude(this$1._geometries[i].getEnvelopeInternal());
98200             }
98201             return envelope
98202           };
98203           GeometryCollection.prototype.getGeometryN = function getGeometryN (n) {
98204             return this._geometries[n]
98205           };
98206           GeometryCollection.prototype.getSortIndex = function getSortIndex () {
98207             return Geometry$$1.SORTINDEX_GEOMETRYCOLLECTION
98208           };
98209           GeometryCollection.prototype.getCoordinates = function getCoordinates () {
98210             var this$1 = this;
98211
98212             var coordinates = new Array(this.getNumPoints()).fill(null);
98213             var k = -1;
98214             for (var i = 0; i < this._geometries.length; i++) {
98215               var childCoordinates = this$1._geometries[i].getCoordinates();
98216               for (var j = 0; j < childCoordinates.length; j++) {
98217                 k++;
98218                 coordinates[k] = childCoordinates[j];
98219               }
98220             }
98221             return coordinates
98222           };
98223           GeometryCollection.prototype.getArea = function getArea () {
98224             var this$1 = this;
98225
98226             var area = 0.0;
98227             for (var i = 0; i < this._geometries.length; i++) {
98228               area += this$1._geometries[i].getArea();
98229             }
98230             return area
98231           };
98232           GeometryCollection.prototype.equalsExact = function equalsExact () {
98233             var this$1 = this;
98234
98235             if (arguments.length === 2) {
98236               var other = arguments[0];
98237               var tolerance = arguments[1];
98238               if (!this.isEquivalentClass(other)) {
98239                 return false
98240               }
98241               var otherCollection = other;
98242               if (this._geometries.length !== otherCollection._geometries.length) {
98243                 return false
98244               }
98245               for (var i = 0; i < this._geometries.length; i++) {
98246                 if (!this$1._geometries[i].equalsExact(otherCollection._geometries[i], tolerance)) {
98247                   return false
98248                 }
98249               }
98250               return true
98251             } else { return Geometry$$1.prototype.equalsExact.apply(this, arguments) }
98252           };
98253           GeometryCollection.prototype.normalize = function normalize () {
98254             var this$1 = this;
98255
98256             for (var i = 0; i < this._geometries.length; i++) {
98257               this$1._geometries[i].normalize();
98258             }
98259             Arrays.sort(this._geometries);
98260           };
98261           GeometryCollection.prototype.getCoordinate = function getCoordinate () {
98262             if (this.isEmpty()) { return null }
98263             return this._geometries[0].getCoordinate()
98264           };
98265           GeometryCollection.prototype.getBoundaryDimension = function getBoundaryDimension () {
98266             var this$1 = this;
98267
98268             var dimension = Dimension.FALSE;
98269             for (var i = 0; i < this._geometries.length; i++) {
98270               dimension = Math.max(dimension, this$1._geometries[i].getBoundaryDimension());
98271             }
98272             return dimension
98273           };
98274           GeometryCollection.prototype.getDimension = function getDimension () {
98275             var this$1 = this;
98276
98277             var dimension = Dimension.FALSE;
98278             for (var i = 0; i < this._geometries.length; i++) {
98279               dimension = Math.max(dimension, this$1._geometries[i].getDimension());
98280             }
98281             return dimension
98282           };
98283           GeometryCollection.prototype.getLength = function getLength () {
98284             var this$1 = this;
98285
98286             var sum = 0.0;
98287             for (var i = 0; i < this._geometries.length; i++) {
98288               sum += this$1._geometries[i].getLength();
98289             }
98290             return sum
98291           };
98292           GeometryCollection.prototype.getNumPoints = function getNumPoints () {
98293             var this$1 = this;
98294
98295             var numPoints = 0;
98296             for (var i = 0; i < this._geometries.length; i++) {
98297               numPoints += this$1._geometries[i].getNumPoints();
98298             }
98299             return numPoints
98300           };
98301           GeometryCollection.prototype.getNumGeometries = function getNumGeometries () {
98302             return this._geometries.length
98303           };
98304           GeometryCollection.prototype.reverse = function reverse () {
98305             var this$1 = this;
98306
98307             var n = this._geometries.length;
98308             var revGeoms = new Array(n).fill(null);
98309             for (var i = 0; i < this._geometries.length; i++) {
98310               revGeoms[i] = this$1._geometries[i].reverse();
98311             }
98312             return this.getFactory().createGeometryCollection(revGeoms)
98313           };
98314           GeometryCollection.prototype.compareToSameClass = function compareToSameClass () {
98315             var this$1 = this;
98316
98317             if (arguments.length === 1) {
98318               var o = arguments[0];
98319               var theseElements = new TreeSet(Arrays.asList(this._geometries));
98320               var otherElements = new TreeSet(Arrays.asList(o._geometries));
98321               return this.compare(theseElements, otherElements)
98322             } else if (arguments.length === 2) {
98323               var o$1 = arguments[0];
98324               var comp = arguments[1];
98325               var gc = o$1;
98326               var n1 = this.getNumGeometries();
98327               var n2 = gc.getNumGeometries();
98328               var i = 0;
98329               while (i < n1 && i < n2) {
98330                 var thisGeom = this$1.getGeometryN(i);
98331                 var otherGeom = gc.getGeometryN(i);
98332                 var holeComp = thisGeom.compareToSameClass(otherGeom, comp);
98333                 if (holeComp !== 0) { return holeComp }
98334                 i++;
98335               }
98336               if (i < n1) { return 1 }
98337               if (i < n2) { return -1 }
98338               return 0
98339             }
98340           };
98341           GeometryCollection.prototype.apply = function apply () {
98342             var this$1 = this;
98343
98344             if (hasInterface(arguments[0], CoordinateFilter)) {
98345               var filter = arguments[0];
98346               for (var i = 0; i < this._geometries.length; i++) {
98347                 this$1._geometries[i].apply(filter);
98348               }
98349             } else if (hasInterface(arguments[0], CoordinateSequenceFilter)) {
98350               var filter$1 = arguments[0];
98351               if (this._geometries.length === 0) { return null }
98352               for (var i$1 = 0; i$1 < this._geometries.length; i$1++) {
98353                 this$1._geometries[i$1].apply(filter$1);
98354                 if (filter$1.isDone()) {
98355                   break
98356                 }
98357               }
98358               if (filter$1.isGeometryChanged()) { this.geometryChanged(); }
98359             } else if (hasInterface(arguments[0], GeometryFilter)) {
98360               var filter$2 = arguments[0];
98361               filter$2.filter(this);
98362               for (var i$2 = 0; i$2 < this._geometries.length; i$2++) {
98363                 this$1._geometries[i$2].apply(filter$2);
98364               }
98365             } else if (hasInterface(arguments[0], GeometryComponentFilter)) {
98366               var filter$3 = arguments[0];
98367               filter$3.filter(this);
98368               for (var i$3 = 0; i$3 < this._geometries.length; i$3++) {
98369                 this$1._geometries[i$3].apply(filter$3);
98370               }
98371             }
98372           };
98373           GeometryCollection.prototype.getBoundary = function getBoundary () {
98374             this.checkNotGeometryCollection(this);
98375             Assert.shouldNeverReachHere();
98376             return null
98377           };
98378           GeometryCollection.prototype.clone = function clone () {
98379             var this$1 = this;
98380
98381             var gc = Geometry$$1.prototype.clone.call(this);
98382             gc._geometries = new Array(this._geometries.length).fill(null);
98383             for (var i = 0; i < this._geometries.length; i++) {
98384               gc._geometries[i] = this$1._geometries[i].clone();
98385             }
98386             return gc
98387           };
98388           GeometryCollection.prototype.getGeometryType = function getGeometryType () {
98389             return 'GeometryCollection'
98390           };
98391           GeometryCollection.prototype.copy = function copy () {
98392             var this$1 = this;
98393
98394             var geometries = new Array(this._geometries.length).fill(null);
98395             for (var i = 0; i < geometries.length; i++) {
98396               geometries[i] = this$1._geometries[i].copy();
98397             }
98398             return new GeometryCollection(geometries, this._factory)
98399           };
98400           GeometryCollection.prototype.isEmpty = function isEmpty () {
98401             var this$1 = this;
98402
98403             for (var i = 0; i < this._geometries.length; i++) {
98404               if (!this$1._geometries[i].isEmpty()) {
98405                 return false
98406               }
98407             }
98408             return true
98409           };
98410           GeometryCollection.prototype.interfaces_ = function interfaces_ () {
98411             return []
98412           };
98413           GeometryCollection.prototype.getClass = function getClass () {
98414             return GeometryCollection
98415           };
98416           staticAccessors.serialVersionUID.get = function () { return -5694727726395021467 };
98417
98418           Object.defineProperties( GeometryCollection, staticAccessors );
98419
98420           return GeometryCollection;
98421         }(Geometry));
98422
98423         var MultiLineString = (function (GeometryCollection$$1) {
98424           function MultiLineString () {
98425             GeometryCollection$$1.apply(this, arguments);
98426           }
98427
98428           if ( GeometryCollection$$1 ) { MultiLineString.__proto__ = GeometryCollection$$1; }
98429           MultiLineString.prototype = Object.create( GeometryCollection$$1 && GeometryCollection$$1.prototype );
98430           MultiLineString.prototype.constructor = MultiLineString;
98431
98432           var staticAccessors = { serialVersionUID: { configurable: true } };
98433
98434           MultiLineString.prototype.getSortIndex = function getSortIndex () {
98435             return Geometry.SORTINDEX_MULTILINESTRING
98436           };
98437           MultiLineString.prototype.equalsExact = function equalsExact () {
98438             if (arguments.length === 2) {
98439               var other = arguments[0];
98440               var tolerance = arguments[1];
98441               if (!this.isEquivalentClass(other)) {
98442                 return false
98443               }
98444               return GeometryCollection$$1.prototype.equalsExact.call(this, other, tolerance)
98445             } else { return GeometryCollection$$1.prototype.equalsExact.apply(this, arguments) }
98446           };
98447           MultiLineString.prototype.getBoundaryDimension = function getBoundaryDimension () {
98448             if (this.isClosed()) {
98449               return Dimension.FALSE
98450             }
98451             return 0
98452           };
98453           MultiLineString.prototype.isClosed = function isClosed () {
98454             var this$1 = this;
98455
98456             if (this.isEmpty()) {
98457               return false
98458             }
98459             for (var i = 0; i < this._geometries.length; i++) {
98460               if (!this$1._geometries[i].isClosed()) {
98461                 return false
98462               }
98463             }
98464             return true
98465           };
98466           MultiLineString.prototype.getDimension = function getDimension () {
98467             return 1
98468           };
98469           MultiLineString.prototype.reverse = function reverse () {
98470             var this$1 = this;
98471
98472             var nLines = this._geometries.length;
98473             var revLines = new Array(nLines).fill(null);
98474             for (var i = 0; i < this._geometries.length; i++) {
98475               revLines[nLines - 1 - i] = this$1._geometries[i].reverse();
98476             }
98477             return this.getFactory().createMultiLineString(revLines)
98478           };
98479           MultiLineString.prototype.getBoundary = function getBoundary () {
98480             return new BoundaryOp(this).getBoundary()
98481           };
98482           MultiLineString.prototype.getGeometryType = function getGeometryType () {
98483             return 'MultiLineString'
98484           };
98485           MultiLineString.prototype.copy = function copy () {
98486             var this$1 = this;
98487
98488             var lineStrings = new Array(this._geometries.length).fill(null);
98489             for (var i = 0; i < lineStrings.length; i++) {
98490               lineStrings[i] = this$1._geometries[i].copy();
98491             }
98492             return new MultiLineString(lineStrings, this._factory)
98493           };
98494           MultiLineString.prototype.interfaces_ = function interfaces_ () {
98495             return [Lineal]
98496           };
98497           MultiLineString.prototype.getClass = function getClass () {
98498             return MultiLineString
98499           };
98500           staticAccessors.serialVersionUID.get = function () { return 8166665132445433741 };
98501
98502           Object.defineProperties( MultiLineString, staticAccessors );
98503
98504           return MultiLineString;
98505         }(GeometryCollection));
98506
98507         var BoundaryOp = function BoundaryOp () {
98508           this._geom = null;
98509           this._geomFact = null;
98510           this._bnRule = null;
98511           this._endpointMap = null;
98512           if (arguments.length === 1) {
98513             var geom = arguments[0];
98514             var bnRule = BoundaryNodeRule.MOD2_BOUNDARY_RULE;
98515             this._geom = geom;
98516             this._geomFact = geom.getFactory();
98517             this._bnRule = bnRule;
98518           } else if (arguments.length === 2) {
98519             var geom$1 = arguments[0];
98520             var bnRule$1 = arguments[1];
98521             this._geom = geom$1;
98522             this._geomFact = geom$1.getFactory();
98523             this._bnRule = bnRule$1;
98524           }
98525         };
98526         BoundaryOp.prototype.boundaryMultiLineString = function boundaryMultiLineString (mLine) {
98527           if (this._geom.isEmpty()) {
98528             return this.getEmptyMultiPoint()
98529           }
98530           var bdyPts = this.computeBoundaryCoordinates(mLine);
98531           if (bdyPts.length === 1) {
98532             return this._geomFact.createPoint(bdyPts[0])
98533           }
98534           return this._geomFact.createMultiPointFromCoords(bdyPts)
98535         };
98536         BoundaryOp.prototype.getBoundary = function getBoundary () {
98537           if (this._geom instanceof LineString) { return this.boundaryLineString(this._geom) }
98538           if (this._geom instanceof MultiLineString) { return this.boundaryMultiLineString(this._geom) }
98539           return this._geom.getBoundary()
98540         };
98541         BoundaryOp.prototype.boundaryLineString = function boundaryLineString (line) {
98542           if (this._geom.isEmpty()) {
98543             return this.getEmptyMultiPoint()
98544           }
98545           if (line.isClosed()) {
98546             var closedEndpointOnBoundary = this._bnRule.isInBoundary(2);
98547             if (closedEndpointOnBoundary) {
98548               return line.getStartPoint()
98549             } else {
98550               return this._geomFact.createMultiPoint()
98551             }
98552           }
98553           return this._geomFact.createMultiPoint([line.getStartPoint(), line.getEndPoint()])
98554         };
98555         BoundaryOp.prototype.getEmptyMultiPoint = function getEmptyMultiPoint () {
98556           return this._geomFact.createMultiPoint()
98557         };
98558         BoundaryOp.prototype.computeBoundaryCoordinates = function computeBoundaryCoordinates (mLine) {
98559             var this$1 = this;
98560
98561           var bdyPts = new ArrayList();
98562           this._endpointMap = new TreeMap();
98563           for (var i = 0; i < mLine.getNumGeometries(); i++) {
98564             var line = mLine.getGeometryN(i);
98565             if (line.getNumPoints() === 0) { continue }
98566             this$1.addEndpoint(line.getCoordinateN(0));
98567             this$1.addEndpoint(line.getCoordinateN(line.getNumPoints() - 1));
98568           }
98569           for (var it = this._endpointMap.entrySet().iterator(); it.hasNext();) {
98570             var entry = it.next();
98571             var counter = entry.getValue();
98572             var valence = counter.count;
98573             if (this$1._bnRule.isInBoundary(valence)) {
98574               bdyPts.add(entry.getKey());
98575             }
98576           }
98577           return CoordinateArrays.toCoordinateArray(bdyPts)
98578         };
98579         BoundaryOp.prototype.addEndpoint = function addEndpoint (pt) {
98580           var counter = this._endpointMap.get(pt);
98581           if (counter === null) {
98582             counter = new Counter();
98583             this._endpointMap.put(pt, counter);
98584           }
98585           counter.count++;
98586         };
98587         BoundaryOp.prototype.interfaces_ = function interfaces_ () {
98588           return []
98589         };
98590         BoundaryOp.prototype.getClass = function getClass () {
98591           return BoundaryOp
98592         };
98593         BoundaryOp.getBoundary = function getBoundary () {
98594           if (arguments.length === 1) {
98595             var g = arguments[0];
98596             var bop = new BoundaryOp(g);
98597             return bop.getBoundary()
98598           } else if (arguments.length === 2) {
98599             var g$1 = arguments[0];
98600             var bnRule = arguments[1];
98601             var bop$1 = new BoundaryOp(g$1, bnRule);
98602             return bop$1.getBoundary()
98603           }
98604         };
98605
98606         var Counter = function Counter () {
98607           this.count = null;
98608         };
98609         Counter.prototype.interfaces_ = function interfaces_ () {
98610           return []
98611         };
98612         Counter.prototype.getClass = function getClass () {
98613           return Counter
98614         };
98615
98616         // boundary
98617
98618         function PrintStream () {}
98619
98620         function StringReader () {}
98621
98622         var DecimalFormat = function DecimalFormat () {};
98623
98624         function ByteArrayOutputStream () {}
98625
98626         function IOException () {}
98627
98628         function LineNumberReader () {}
98629
98630         var StringUtil = function StringUtil () {};
98631
98632         var staticAccessors$15 = { NEWLINE: { configurable: true },SIMPLE_ORDINATE_FORMAT: { configurable: true } };
98633
98634         StringUtil.prototype.interfaces_ = function interfaces_ () {
98635           return []
98636         };
98637         StringUtil.prototype.getClass = function getClass () {
98638           return StringUtil
98639         };
98640         StringUtil.chars = function chars (c, n) {
98641           var ch = new Array(n).fill(null);
98642           for (var i = 0; i < n; i++) {
98643             ch[i] = c;
98644           }
98645           return String(ch)
98646         };
98647         StringUtil.getStackTrace = function getStackTrace () {
98648           if (arguments.length === 1) {
98649             var t = arguments[0];
98650             var os = new ByteArrayOutputStream();
98651             var ps = new PrintStream(os);
98652             t.printStackTrace(ps);
98653             return os.toString()
98654           } else if (arguments.length === 2) {
98655             var t$1 = arguments[0];
98656             var depth = arguments[1];
98657             var stackTrace = '';
98658             var stringReader = new StringReader(StringUtil.getStackTrace(t$1));
98659             var lineNumberReader = new LineNumberReader(stringReader);
98660             for (var i = 0; i < depth; i++) {
98661               try {
98662                 stackTrace += lineNumberReader.readLine() + StringUtil.NEWLINE;
98663               } catch (e) {
98664                 if (e instanceof IOException) {
98665                   Assert.shouldNeverReachHere();
98666                 } else { throw e }
98667               } finally {}
98668             }
98669             return stackTrace
98670           }
98671         };
98672         StringUtil.split = function split (s, separator) {
98673           var separatorlen = separator.length;
98674           var tokenList = new ArrayList();
98675           var tmpString = '' + s;
98676           var pos = tmpString.indexOf(separator);
98677           while (pos >= 0) {
98678             var token = tmpString.substring(0, pos);
98679             tokenList.add(token);
98680             tmpString = tmpString.substring(pos + separatorlen);
98681             pos = tmpString.indexOf(separator);
98682           }
98683           if (tmpString.length > 0) { tokenList.add(tmpString); }
98684           var res = new Array(tokenList.size()).fill(null);
98685           for (var i = 0; i < res.length; i++) {
98686             res[i] = tokenList.get(i);
98687           }
98688           return res
98689         };
98690         StringUtil.toString = function toString () {
98691           if (arguments.length === 1) {
98692             var d = arguments[0];
98693             return StringUtil.SIMPLE_ORDINATE_FORMAT.format(d)
98694           }
98695         };
98696         StringUtil.spaces = function spaces (n) {
98697           return StringUtil.chars(' ', n)
98698         };
98699         staticAccessors$15.NEWLINE.get = function () { return System.getProperty('line.separator') };
98700         staticAccessors$15.SIMPLE_ORDINATE_FORMAT.get = function () { return new DecimalFormat('0.#') };
98701
98702         Object.defineProperties( StringUtil, staticAccessors$15 );
98703
98704         var CoordinateSequences = function CoordinateSequences () {};
98705
98706         CoordinateSequences.prototype.interfaces_ = function interfaces_ () {
98707           return []
98708         };
98709         CoordinateSequences.prototype.getClass = function getClass () {
98710           return CoordinateSequences
98711         };
98712         CoordinateSequences.copyCoord = function copyCoord (src, srcPos, dest, destPos) {
98713           var minDim = Math.min(src.getDimension(), dest.getDimension());
98714           for (var dim = 0; dim < minDim; dim++) {
98715             dest.setOrdinate(destPos, dim, src.getOrdinate(srcPos, dim));
98716           }
98717         };
98718         CoordinateSequences.isRing = function isRing (seq) {
98719           var n = seq.size();
98720           if (n === 0) { return true }
98721           if (n <= 3) { return false }
98722           return seq.getOrdinate(0, CoordinateSequence.X) === seq.getOrdinate(n - 1, CoordinateSequence.X) && seq.getOrdinate(0, CoordinateSequence.Y) === seq.getOrdinate(n - 1, CoordinateSequence.Y)
98723         };
98724         CoordinateSequences.isEqual = function isEqual (cs1, cs2) {
98725           var cs1Size = cs1.size();
98726           var cs2Size = cs2.size();
98727           if (cs1Size !== cs2Size) { return false }
98728           var dim = Math.min(cs1.getDimension(), cs2.getDimension());
98729           for (var i = 0; i < cs1Size; i++) {
98730             for (var d = 0; d < dim; d++) {
98731               var v1 = cs1.getOrdinate(i, d);
98732               var v2 = cs2.getOrdinate(i, d);
98733               if (cs1.getOrdinate(i, d) === cs2.getOrdinate(i, d)) { continue }
98734               if (Double.isNaN(v1) && Double.isNaN(v2)) { continue }
98735               return false
98736             }
98737           }
98738           return true
98739         };
98740         CoordinateSequences.extend = function extend (fact, seq, size) {
98741           var newseq = fact.create(size, seq.getDimension());
98742           var n = seq.size();
98743           CoordinateSequences.copy(seq, 0, newseq, 0, n);
98744           if (n > 0) {
98745             for (var i = n; i < size; i++) { CoordinateSequences.copy(seq, n - 1, newseq, i, 1); }
98746           }
98747           return newseq
98748         };
98749         CoordinateSequences.reverse = function reverse (seq) {
98750           var last = seq.size() - 1;
98751           var mid = Math.trunc(last / 2);
98752           for (var i = 0; i <= mid; i++) {
98753             CoordinateSequences.swap(seq, i, last - i);
98754           }
98755         };
98756         CoordinateSequences.swap = function swap (seq, i, j) {
98757           if (i === j) { return null }
98758           for (var dim = 0; dim < seq.getDimension(); dim++) {
98759             var tmp = seq.getOrdinate(i, dim);
98760             seq.setOrdinate(i, dim, seq.getOrdinate(j, dim));
98761             seq.setOrdinate(j, dim, tmp);
98762           }
98763         };
98764         CoordinateSequences.copy = function copy (src, srcPos, dest, destPos, length) {
98765           for (var i = 0; i < length; i++) {
98766             CoordinateSequences.copyCoord(src, srcPos + i, dest, destPos + i);
98767           }
98768         };
98769         CoordinateSequences.toString = function toString () {
98770           if (arguments.length === 1) {
98771             var cs = arguments[0];
98772             var size = cs.size();
98773             if (size === 0) { return '()' }
98774             var dim = cs.getDimension();
98775             var buf = new StringBuffer();
98776             buf.append('(');
98777             for (var i = 0; i < size; i++) {
98778               if (i > 0) { buf.append(' '); }
98779               for (var d = 0; d < dim; d++) {
98780                 if (d > 0) { buf.append(','); }
98781                 buf.append(StringUtil.toString(cs.getOrdinate(i, d)));
98782               }
98783             }
98784             buf.append(')');
98785             return buf.toString()
98786           }
98787         };
98788         CoordinateSequences.ensureValidRing = function ensureValidRing (fact, seq) {
98789           var n = seq.size();
98790           if (n === 0) { return seq }
98791           if (n <= 3) { return CoordinateSequences.createClosedRing(fact, seq, 4) }
98792           var isClosed = seq.getOrdinate(0, CoordinateSequence.X) === seq.getOrdinate(n - 1, CoordinateSequence.X) && seq.getOrdinate(0, CoordinateSequence.Y) === seq.getOrdinate(n - 1, CoordinateSequence.Y);
98793           if (isClosed) { return seq }
98794           return CoordinateSequences.createClosedRing(fact, seq, n + 1)
98795         };
98796         CoordinateSequences.createClosedRing = function createClosedRing (fact, seq, size) {
98797           var newseq = fact.create(size, seq.getDimension());
98798           var n = seq.size();
98799           CoordinateSequences.copy(seq, 0, newseq, 0, n);
98800           for (var i = n; i < size; i++) { CoordinateSequences.copy(seq, 0, newseq, i, 1); }
98801           return newseq
98802         };
98803
98804         var LineString = (function (Geometry$$1) {
98805           function LineString (points, factory) {
98806             Geometry$$1.call(this, factory);
98807             this._points = null;
98808             this.init(points);
98809           }
98810
98811           if ( Geometry$$1 ) { LineString.__proto__ = Geometry$$1; }
98812           LineString.prototype = Object.create( Geometry$$1 && Geometry$$1.prototype );
98813           LineString.prototype.constructor = LineString;
98814
98815           var staticAccessors = { serialVersionUID: { configurable: true } };
98816           LineString.prototype.computeEnvelopeInternal = function computeEnvelopeInternal () {
98817             if (this.isEmpty()) {
98818               return new Envelope()
98819             }
98820             return this._points.expandEnvelope(new Envelope())
98821           };
98822           LineString.prototype.isRing = function isRing () {
98823             return this.isClosed() && this.isSimple()
98824           };
98825           LineString.prototype.getSortIndex = function getSortIndex () {
98826             return Geometry$$1.SORTINDEX_LINESTRING
98827           };
98828           LineString.prototype.getCoordinates = function getCoordinates () {
98829             return this._points.toCoordinateArray()
98830           };
98831           LineString.prototype.equalsExact = function equalsExact () {
98832             var this$1 = this;
98833
98834             if (arguments.length === 2) {
98835               var other = arguments[0];
98836               var tolerance = arguments[1];
98837               if (!this.isEquivalentClass(other)) {
98838                 return false
98839               }
98840               var otherLineString = other;
98841               if (this._points.size() !== otherLineString._points.size()) {
98842                 return false
98843               }
98844               for (var i = 0; i < this._points.size(); i++) {
98845                 if (!this$1.equal(this$1._points.getCoordinate(i), otherLineString._points.getCoordinate(i), tolerance)) {
98846                   return false
98847                 }
98848               }
98849               return true
98850             } else { return Geometry$$1.prototype.equalsExact.apply(this, arguments) }
98851           };
98852           LineString.prototype.normalize = function normalize () {
98853             var this$1 = this;
98854
98855             for (var i = 0; i < Math.trunc(this._points.size() / 2); i++) {
98856               var j = this$1._points.size() - 1 - i;
98857               if (!this$1._points.getCoordinate(i).equals(this$1._points.getCoordinate(j))) {
98858                 if (this$1._points.getCoordinate(i).compareTo(this$1._points.getCoordinate(j)) > 0) {
98859                   CoordinateSequences.reverse(this$1._points);
98860                 }
98861                 return null
98862               }
98863             }
98864           };
98865           LineString.prototype.getCoordinate = function getCoordinate () {
98866             if (this.isEmpty()) { return null }
98867             return this._points.getCoordinate(0)
98868           };
98869           LineString.prototype.getBoundaryDimension = function getBoundaryDimension () {
98870             if (this.isClosed()) {
98871               return Dimension.FALSE
98872             }
98873             return 0
98874           };
98875           LineString.prototype.isClosed = function isClosed () {
98876             if (this.isEmpty()) {
98877               return false
98878             }
98879             return this.getCoordinateN(0).equals2D(this.getCoordinateN(this.getNumPoints() - 1))
98880           };
98881           LineString.prototype.getEndPoint = function getEndPoint () {
98882             if (this.isEmpty()) {
98883               return null
98884             }
98885             return this.getPointN(this.getNumPoints() - 1)
98886           };
98887           LineString.prototype.getDimension = function getDimension () {
98888             return 1
98889           };
98890           LineString.prototype.getLength = function getLength () {
98891             return CGAlgorithms.computeLength(this._points)
98892           };
98893           LineString.prototype.getNumPoints = function getNumPoints () {
98894             return this._points.size()
98895           };
98896           LineString.prototype.reverse = function reverse () {
98897             var seq = this._points.copy();
98898             CoordinateSequences.reverse(seq);
98899             var revLine = this.getFactory().createLineString(seq);
98900             return revLine
98901           };
98902           LineString.prototype.compareToSameClass = function compareToSameClass () {
98903             var this$1 = this;
98904
98905             if (arguments.length === 1) {
98906               var o = arguments[0];
98907               var line = o;
98908               var i = 0;
98909               var j = 0;
98910               while (i < this._points.size() && j < line._points.size()) {
98911                 var comparison = this$1._points.getCoordinate(i).compareTo(line._points.getCoordinate(j));
98912                 if (comparison !== 0) {
98913                   return comparison
98914                 }
98915                 i++;
98916                 j++;
98917               }
98918               if (i < this._points.size()) {
98919                 return 1
98920               }
98921               if (j < line._points.size()) {
98922                 return -1
98923               }
98924               return 0
98925             } else if (arguments.length === 2) {
98926               var o$1 = arguments[0];
98927               var comp = arguments[1];
98928               var line$1 = o$1;
98929               return comp.compare(this._points, line$1._points)
98930             }
98931           };
98932           LineString.prototype.apply = function apply () {
98933             var this$1 = this;
98934
98935             if (hasInterface(arguments[0], CoordinateFilter)) {
98936               var filter = arguments[0];
98937               for (var i = 0; i < this._points.size(); i++) {
98938                 filter.filter(this$1._points.getCoordinate(i));
98939               }
98940             } else if (hasInterface(arguments[0], CoordinateSequenceFilter)) {
98941               var filter$1 = arguments[0];
98942               if (this._points.size() === 0) { return null }
98943               for (var i$1 = 0; i$1 < this._points.size(); i$1++) {
98944                 filter$1.filter(this$1._points, i$1);
98945                 if (filter$1.isDone()) { break }
98946               }
98947               if (filter$1.isGeometryChanged()) { this.geometryChanged(); }
98948             } else if (hasInterface(arguments[0], GeometryFilter)) {
98949               var filter$2 = arguments[0];
98950               filter$2.filter(this);
98951             } else if (hasInterface(arguments[0], GeometryComponentFilter)) {
98952               var filter$3 = arguments[0];
98953               filter$3.filter(this);
98954             }
98955           };
98956           LineString.prototype.getBoundary = function getBoundary () {
98957             return new BoundaryOp(this).getBoundary()
98958           };
98959           LineString.prototype.isEquivalentClass = function isEquivalentClass (other) {
98960             return other instanceof LineString
98961           };
98962           LineString.prototype.clone = function clone () {
98963             var ls = Geometry$$1.prototype.clone.call(this);
98964             ls._points = this._points.clone();
98965             return ls
98966           };
98967           LineString.prototype.getCoordinateN = function getCoordinateN (n) {
98968             return this._points.getCoordinate(n)
98969           };
98970           LineString.prototype.getGeometryType = function getGeometryType () {
98971             return 'LineString'
98972           };
98973           LineString.prototype.copy = function copy () {
98974             return new LineString(this._points.copy(), this._factory)
98975           };
98976           LineString.prototype.getCoordinateSequence = function getCoordinateSequence () {
98977             return this._points
98978           };
98979           LineString.prototype.isEmpty = function isEmpty () {
98980             return this._points.size() === 0
98981           };
98982           LineString.prototype.init = function init (points) {
98983             if (points === null) {
98984               points = this.getFactory().getCoordinateSequenceFactory().create([]);
98985             }
98986             if (points.size() === 1) {
98987               throw new IllegalArgumentException('Invalid number of points in LineString (found ' + points.size() + ' - must be 0 or >= 2)')
98988             }
98989             this._points = points;
98990           };
98991           LineString.prototype.isCoordinate = function isCoordinate (pt) {
98992             var this$1 = this;
98993
98994             for (var i = 0; i < this._points.size(); i++) {
98995               if (this$1._points.getCoordinate(i).equals(pt)) {
98996                 return true
98997               }
98998             }
98999             return false
99000           };
99001           LineString.prototype.getStartPoint = function getStartPoint () {
99002             if (this.isEmpty()) {
99003               return null
99004             }
99005             return this.getPointN(0)
99006           };
99007           LineString.prototype.getPointN = function getPointN (n) {
99008             return this.getFactory().createPoint(this._points.getCoordinate(n))
99009           };
99010           LineString.prototype.interfaces_ = function interfaces_ () {
99011             return [Lineal]
99012           };
99013           LineString.prototype.getClass = function getClass () {
99014             return LineString
99015           };
99016           staticAccessors.serialVersionUID.get = function () { return 3110669828065365560 };
99017
99018           Object.defineProperties( LineString, staticAccessors );
99019
99020           return LineString;
99021         }(Geometry));
99022
99023         var Puntal = function Puntal () {};
99024
99025         Puntal.prototype.interfaces_ = function interfaces_ () {
99026           return []
99027         };
99028         Puntal.prototype.getClass = function getClass () {
99029           return Puntal
99030         };
99031
99032         var Point$1 = (function (Geometry$$1) {
99033           function Point (coordinates, factory) {
99034             Geometry$$1.call(this, factory);
99035             this._coordinates = coordinates || null;
99036             this.init(this._coordinates);
99037           }
99038
99039           if ( Geometry$$1 ) { Point.__proto__ = Geometry$$1; }
99040           Point.prototype = Object.create( Geometry$$1 && Geometry$$1.prototype );
99041           Point.prototype.constructor = Point;
99042
99043           var staticAccessors = { serialVersionUID: { configurable: true } };
99044           Point.prototype.computeEnvelopeInternal = function computeEnvelopeInternal () {
99045             if (this.isEmpty()) {
99046               return new Envelope()
99047             }
99048             var env = new Envelope();
99049             env.expandToInclude(this._coordinates.getX(0), this._coordinates.getY(0));
99050             return env
99051           };
99052           Point.prototype.getSortIndex = function getSortIndex () {
99053             return Geometry$$1.SORTINDEX_POINT
99054           };
99055           Point.prototype.getCoordinates = function getCoordinates () {
99056             return this.isEmpty() ? [] : [this.getCoordinate()]
99057           };
99058           Point.prototype.equalsExact = function equalsExact () {
99059             if (arguments.length === 2) {
99060               var other = arguments[0];
99061               var tolerance = arguments[1];
99062               if (!this.isEquivalentClass(other)) {
99063                 return false
99064               }
99065               if (this.isEmpty() && other.isEmpty()) {
99066                 return true
99067               }
99068               if (this.isEmpty() !== other.isEmpty()) {
99069                 return false
99070               }
99071               return this.equal(other.getCoordinate(), this.getCoordinate(), tolerance)
99072             } else { return Geometry$$1.prototype.equalsExact.apply(this, arguments) }
99073           };
99074           Point.prototype.normalize = function normalize () {};
99075           Point.prototype.getCoordinate = function getCoordinate () {
99076             return this._coordinates.size() !== 0 ? this._coordinates.getCoordinate(0) : null
99077           };
99078           Point.prototype.getBoundaryDimension = function getBoundaryDimension () {
99079             return Dimension.FALSE
99080           };
99081           Point.prototype.getDimension = function getDimension () {
99082             return 0
99083           };
99084           Point.prototype.getNumPoints = function getNumPoints () {
99085             return this.isEmpty() ? 0 : 1
99086           };
99087           Point.prototype.reverse = function reverse () {
99088             return this.copy()
99089           };
99090           Point.prototype.getX = function getX () {
99091             if (this.getCoordinate() === null) {
99092               throw new Error('getX called on empty Point')
99093             }
99094             return this.getCoordinate().x
99095           };
99096           Point.prototype.compareToSameClass = function compareToSameClass () {
99097             if (arguments.length === 1) {
99098               var other = arguments[0];
99099               var point$1 = other;
99100               return this.getCoordinate().compareTo(point$1.getCoordinate())
99101             } else if (arguments.length === 2) {
99102               var other$1 = arguments[0];
99103               var comp = arguments[1];
99104               var point = other$1;
99105               return comp.compare(this._coordinates, point._coordinates)
99106             }
99107           };
99108           Point.prototype.apply = function apply () {
99109             if (hasInterface(arguments[0], CoordinateFilter)) {
99110               var filter = arguments[0];
99111               if (this.isEmpty()) {
99112                 return null
99113               }
99114               filter.filter(this.getCoordinate());
99115             } else if (hasInterface(arguments[0], CoordinateSequenceFilter)) {
99116               var filter$1 = arguments[0];
99117               if (this.isEmpty()) { return null }
99118               filter$1.filter(this._coordinates, 0);
99119               if (filter$1.isGeometryChanged()) { this.geometryChanged(); }
99120             } else if (hasInterface(arguments[0], GeometryFilter)) {
99121               var filter$2 = arguments[0];
99122               filter$2.filter(this);
99123             } else if (hasInterface(arguments[0], GeometryComponentFilter)) {
99124               var filter$3 = arguments[0];
99125               filter$3.filter(this);
99126             }
99127           };
99128           Point.prototype.getBoundary = function getBoundary () {
99129             return this.getFactory().createGeometryCollection(null)
99130           };
99131           Point.prototype.clone = function clone () {
99132             var p = Geometry$$1.prototype.clone.call(this);
99133             p._coordinates = this._coordinates.clone();
99134             return p
99135           };
99136           Point.prototype.getGeometryType = function getGeometryType () {
99137             return 'Point'
99138           };
99139           Point.prototype.copy = function copy () {
99140             return new Point(this._coordinates.copy(), this._factory)
99141           };
99142           Point.prototype.getCoordinateSequence = function getCoordinateSequence () {
99143             return this._coordinates
99144           };
99145           Point.prototype.getY = function getY () {
99146             if (this.getCoordinate() === null) {
99147               throw new Error('getY called on empty Point')
99148             }
99149             return this.getCoordinate().y
99150           };
99151           Point.prototype.isEmpty = function isEmpty () {
99152             return this._coordinates.size() === 0
99153           };
99154           Point.prototype.init = function init (coordinates) {
99155             if (coordinates === null) {
99156               coordinates = this.getFactory().getCoordinateSequenceFactory().create([]);
99157             }
99158             Assert.isTrue(coordinates.size() <= 1);
99159             this._coordinates = coordinates;
99160           };
99161           Point.prototype.isSimple = function isSimple () {
99162             return true
99163           };
99164           Point.prototype.interfaces_ = function interfaces_ () {
99165             return [Puntal]
99166           };
99167           Point.prototype.getClass = function getClass () {
99168             return Point
99169           };
99170           staticAccessors.serialVersionUID.get = function () { return 4902022702746614570 };
99171
99172           Object.defineProperties( Point, staticAccessors );
99173
99174           return Point;
99175         }(Geometry));
99176
99177         var Polygonal = function Polygonal () {};
99178
99179         Polygonal.prototype.interfaces_ = function interfaces_ () {
99180           return []
99181         };
99182         Polygonal.prototype.getClass = function getClass () {
99183           return Polygonal
99184         };
99185
99186         var Polygon = (function (Geometry$$1) {
99187           function Polygon (shell, holes, factory) {
99188             Geometry$$1.call(this, factory);
99189             this._shell = null;
99190             this._holes = null;
99191             if (shell === null) {
99192               shell = this.getFactory().createLinearRing();
99193             }
99194             if (holes === null) {
99195               holes = [];
99196             }
99197             if (Geometry$$1.hasNullElements(holes)) {
99198               throw new IllegalArgumentException('holes must not contain null elements')
99199             }
99200             if (shell.isEmpty() && Geometry$$1.hasNonEmptyElements(holes)) {
99201               throw new IllegalArgumentException('shell is empty but holes are not')
99202             }
99203             this._shell = shell;
99204             this._holes = holes;
99205           }
99206
99207           if ( Geometry$$1 ) { Polygon.__proto__ = Geometry$$1; }
99208           Polygon.prototype = Object.create( Geometry$$1 && Geometry$$1.prototype );
99209           Polygon.prototype.constructor = Polygon;
99210
99211           var staticAccessors = { serialVersionUID: { configurable: true } };
99212           Polygon.prototype.computeEnvelopeInternal = function computeEnvelopeInternal () {
99213             return this._shell.getEnvelopeInternal()
99214           };
99215           Polygon.prototype.getSortIndex = function getSortIndex () {
99216             return Geometry$$1.SORTINDEX_POLYGON
99217           };
99218           Polygon.prototype.getCoordinates = function getCoordinates () {
99219             var this$1 = this;
99220
99221             if (this.isEmpty()) {
99222               return []
99223             }
99224             var coordinates = new Array(this.getNumPoints()).fill(null);
99225             var k = -1;
99226             var shellCoordinates = this._shell.getCoordinates();
99227             for (var x = 0; x < shellCoordinates.length; x++) {
99228               k++;
99229               coordinates[k] = shellCoordinates[x];
99230             }
99231             for (var i = 0; i < this._holes.length; i++) {
99232               var childCoordinates = this$1._holes[i].getCoordinates();
99233               for (var j = 0; j < childCoordinates.length; j++) {
99234                 k++;
99235                 coordinates[k] = childCoordinates[j];
99236               }
99237             }
99238             return coordinates
99239           };
99240           Polygon.prototype.getArea = function getArea () {
99241             var this$1 = this;
99242
99243             var area = 0.0;
99244             area += Math.abs(CGAlgorithms.signedArea(this._shell.getCoordinateSequence()));
99245             for (var i = 0; i < this._holes.length; i++) {
99246               area -= Math.abs(CGAlgorithms.signedArea(this$1._holes[i].getCoordinateSequence()));
99247             }
99248             return area
99249           };
99250           Polygon.prototype.isRectangle = function isRectangle () {
99251             if (this.getNumInteriorRing() !== 0) { return false }
99252             if (this._shell === null) { return false }
99253             if (this._shell.getNumPoints() !== 5) { return false }
99254             var seq = this._shell.getCoordinateSequence();
99255             var env = this.getEnvelopeInternal();
99256             for (var i = 0; i < 5; i++) {
99257               var x = seq.getX(i);
99258               if (!(x === env.getMinX() || x === env.getMaxX())) { return false }
99259               var y = seq.getY(i);
99260               if (!(y === env.getMinY() || y === env.getMaxY())) { return false }
99261             }
99262             var prevX = seq.getX(0);
99263             var prevY = seq.getY(0);
99264             for (var i$1 = 1; i$1 <= 4; i$1++) {
99265               var x$1 = seq.getX(i$1);
99266               var y$1 = seq.getY(i$1);
99267               var xChanged = x$1 !== prevX;
99268               var yChanged = y$1 !== prevY;
99269               if (xChanged === yChanged) { return false }
99270               prevX = x$1;
99271               prevY = y$1;
99272             }
99273             return true
99274           };
99275           Polygon.prototype.equalsExact = function equalsExact () {
99276             var this$1 = this;
99277
99278             if (arguments.length === 2) {
99279               var other = arguments[0];
99280               var tolerance = arguments[1];
99281               if (!this.isEquivalentClass(other)) {
99282                 return false
99283               }
99284               var otherPolygon = other;
99285               var thisShell = this._shell;
99286               var otherPolygonShell = otherPolygon._shell;
99287               if (!thisShell.equalsExact(otherPolygonShell, tolerance)) {
99288                 return false
99289               }
99290               if (this._holes.length !== otherPolygon._holes.length) {
99291                 return false
99292               }
99293               for (var i = 0; i < this._holes.length; i++) {
99294                 if (!this$1._holes[i].equalsExact(otherPolygon._holes[i], tolerance)) {
99295                   return false
99296                 }
99297               }
99298               return true
99299             } else { return Geometry$$1.prototype.equalsExact.apply(this, arguments) }
99300           };
99301           Polygon.prototype.normalize = function normalize () {
99302             var this$1 = this;
99303
99304             if (arguments.length === 0) {
99305               this.normalize(this._shell, true);
99306               for (var i = 0; i < this._holes.length; i++) {
99307                 this$1.normalize(this$1._holes[i], false);
99308               }
99309               Arrays.sort(this._holes);
99310             } else if (arguments.length === 2) {
99311               var ring = arguments[0];
99312               var clockwise = arguments[1];
99313               if (ring.isEmpty()) {
99314                 return null
99315               }
99316               var uniqueCoordinates = new Array(ring.getCoordinates().length - 1).fill(null);
99317               System.arraycopy(ring.getCoordinates(), 0, uniqueCoordinates, 0, uniqueCoordinates.length);
99318               var minCoordinate = CoordinateArrays.minCoordinate(ring.getCoordinates());
99319               CoordinateArrays.scroll(uniqueCoordinates, minCoordinate);
99320               System.arraycopy(uniqueCoordinates, 0, ring.getCoordinates(), 0, uniqueCoordinates.length);
99321               ring.getCoordinates()[uniqueCoordinates.length] = uniqueCoordinates[0];
99322               if (CGAlgorithms.isCCW(ring.getCoordinates()) === clockwise) {
99323                 CoordinateArrays.reverse(ring.getCoordinates());
99324               }
99325             }
99326           };
99327           Polygon.prototype.getCoordinate = function getCoordinate () {
99328             return this._shell.getCoordinate()
99329           };
99330           Polygon.prototype.getNumInteriorRing = function getNumInteriorRing () {
99331             return this._holes.length
99332           };
99333           Polygon.prototype.getBoundaryDimension = function getBoundaryDimension () {
99334             return 1
99335           };
99336           Polygon.prototype.getDimension = function getDimension () {
99337             return 2
99338           };
99339           Polygon.prototype.getLength = function getLength () {
99340             var this$1 = this;
99341
99342             var len = 0.0;
99343             len += this._shell.getLength();
99344             for (var i = 0; i < this._holes.length; i++) {
99345               len += this$1._holes[i].getLength();
99346             }
99347             return len
99348           };
99349           Polygon.prototype.getNumPoints = function getNumPoints () {
99350             var this$1 = this;
99351
99352             var numPoints = this._shell.getNumPoints();
99353             for (var i = 0; i < this._holes.length; i++) {
99354               numPoints += this$1._holes[i].getNumPoints();
99355             }
99356             return numPoints
99357           };
99358           Polygon.prototype.reverse = function reverse () {
99359             var this$1 = this;
99360
99361             var poly = this.copy();
99362             poly._shell = this._shell.copy().reverse();
99363             poly._holes = new Array(this._holes.length).fill(null);
99364             for (var i = 0; i < this._holes.length; i++) {
99365               poly._holes[i] = this$1._holes[i].copy().reverse();
99366             }
99367             return poly
99368           };
99369           Polygon.prototype.convexHull = function convexHull () {
99370             return this.getExteriorRing().convexHull()
99371           };
99372           Polygon.prototype.compareToSameClass = function compareToSameClass () {
99373             var this$1 = this;
99374
99375             if (arguments.length === 1) {
99376               var o = arguments[0];
99377               var thisShell = this._shell;
99378               var otherShell = o._shell;
99379               return thisShell.compareToSameClass(otherShell)
99380             } else if (arguments.length === 2) {
99381               var o$1 = arguments[0];
99382               var comp = arguments[1];
99383               var poly = o$1;
99384               var thisShell$1 = this._shell;
99385               var otherShell$1 = poly._shell;
99386               var shellComp = thisShell$1.compareToSameClass(otherShell$1, comp);
99387               if (shellComp !== 0) { return shellComp }
99388               var nHole1 = this.getNumInteriorRing();
99389               var nHole2 = poly.getNumInteriorRing();
99390               var i = 0;
99391               while (i < nHole1 && i < nHole2) {
99392                 var thisHole = this$1.getInteriorRingN(i);
99393                 var otherHole = poly.getInteriorRingN(i);
99394                 var holeComp = thisHole.compareToSameClass(otherHole, comp);
99395                 if (holeComp !== 0) { return holeComp }
99396                 i++;
99397               }
99398               if (i < nHole1) { return 1 }
99399               if (i < nHole2) { return -1 }
99400               return 0
99401             }
99402           };
99403           Polygon.prototype.apply = function apply (filter) {
99404             var this$1 = this;
99405
99406             if (hasInterface(filter, CoordinateFilter)) {
99407               this._shell.apply(filter);
99408               for (var i$1 = 0; i$1 < this._holes.length; i$1++) {
99409                 this$1._holes[i$1].apply(filter);
99410               }
99411             } else if (hasInterface(filter, CoordinateSequenceFilter)) {
99412               this._shell.apply(filter);
99413               if (!filter.isDone()) {
99414                 for (var i$2 = 0; i$2 < this._holes.length; i$2++) {
99415                   this$1._holes[i$2].apply(filter);
99416                   if (filter.isDone()) { break }
99417                 }
99418               }
99419               if (filter.isGeometryChanged()) { this.geometryChanged(); }
99420             } else if (hasInterface(filter, GeometryFilter)) {
99421               filter.filter(this);
99422             } else if (hasInterface(filter, GeometryComponentFilter)) {
99423               filter.filter(this);
99424               this._shell.apply(filter);
99425               for (var i = 0; i < this._holes.length; i++) {
99426                 this$1._holes[i].apply(filter);
99427               }
99428             }
99429           };
99430           Polygon.prototype.getBoundary = function getBoundary () {
99431             var this$1 = this;
99432
99433             if (this.isEmpty()) {
99434               return this.getFactory().createMultiLineString()
99435             }
99436             var rings = new Array(this._holes.length + 1).fill(null);
99437             rings[0] = this._shell;
99438             for (var i = 0; i < this._holes.length; i++) {
99439               rings[i + 1] = this$1._holes[i];
99440             }
99441             if (rings.length <= 1) { return this.getFactory().createLinearRing(rings[0].getCoordinateSequence()) }
99442             return this.getFactory().createMultiLineString(rings)
99443           };
99444           Polygon.prototype.clone = function clone () {
99445             var this$1 = this;
99446
99447             var poly = Geometry$$1.prototype.clone.call(this);
99448             poly._shell = this._shell.clone();
99449             poly._holes = new Array(this._holes.length).fill(null);
99450             for (var i = 0; i < this._holes.length; i++) {
99451               poly._holes[i] = this$1._holes[i].clone();
99452             }
99453             return poly
99454           };
99455           Polygon.prototype.getGeometryType = function getGeometryType () {
99456             return 'Polygon'
99457           };
99458           Polygon.prototype.copy = function copy () {
99459             var this$1 = this;
99460
99461             var shell = this._shell.copy();
99462             var holes = new Array(this._holes.length).fill(null);
99463             for (var i = 0; i < holes.length; i++) {
99464               holes[i] = this$1._holes[i].copy();
99465             }
99466             return new Polygon(shell, holes, this._factory)
99467           };
99468           Polygon.prototype.getExteriorRing = function getExteriorRing () {
99469             return this._shell
99470           };
99471           Polygon.prototype.isEmpty = function isEmpty () {
99472             return this._shell.isEmpty()
99473           };
99474           Polygon.prototype.getInteriorRingN = function getInteriorRingN (n) {
99475             return this._holes[n]
99476           };
99477           Polygon.prototype.interfaces_ = function interfaces_ () {
99478             return [Polygonal]
99479           };
99480           Polygon.prototype.getClass = function getClass () {
99481             return Polygon
99482           };
99483           staticAccessors.serialVersionUID.get = function () { return -3494792200821764533 };
99484
99485           Object.defineProperties( Polygon, staticAccessors );
99486
99487           return Polygon;
99488         }(Geometry));
99489
99490         var MultiPoint = (function (GeometryCollection$$1) {
99491           function MultiPoint () {
99492             GeometryCollection$$1.apply(this, arguments);
99493           }
99494
99495           if ( GeometryCollection$$1 ) { MultiPoint.__proto__ = GeometryCollection$$1; }
99496           MultiPoint.prototype = Object.create( GeometryCollection$$1 && GeometryCollection$$1.prototype );
99497           MultiPoint.prototype.constructor = MultiPoint;
99498
99499           var staticAccessors = { serialVersionUID: { configurable: true } };
99500
99501           MultiPoint.prototype.getSortIndex = function getSortIndex () {
99502             return Geometry.SORTINDEX_MULTIPOINT
99503           };
99504           MultiPoint.prototype.isValid = function isValid () {
99505             return true
99506           };
99507           MultiPoint.prototype.equalsExact = function equalsExact () {
99508             if (arguments.length === 2) {
99509               var other = arguments[0];
99510               var tolerance = arguments[1];
99511               if (!this.isEquivalentClass(other)) {
99512                 return false
99513               }
99514               return GeometryCollection$$1.prototype.equalsExact.call(this, other, tolerance)
99515             } else { return GeometryCollection$$1.prototype.equalsExact.apply(this, arguments) }
99516           };
99517           MultiPoint.prototype.getCoordinate = function getCoordinate () {
99518             if (arguments.length === 1) {
99519               var n = arguments[0];
99520               return this._geometries[n].getCoordinate()
99521             } else { return GeometryCollection$$1.prototype.getCoordinate.apply(this, arguments) }
99522           };
99523           MultiPoint.prototype.getBoundaryDimension = function getBoundaryDimension () {
99524             return Dimension.FALSE
99525           };
99526           MultiPoint.prototype.getDimension = function getDimension () {
99527             return 0
99528           };
99529           MultiPoint.prototype.getBoundary = function getBoundary () {
99530             return this.getFactory().createGeometryCollection(null)
99531           };
99532           MultiPoint.prototype.getGeometryType = function getGeometryType () {
99533             return 'MultiPoint'
99534           };
99535           MultiPoint.prototype.copy = function copy () {
99536             var this$1 = this;
99537
99538             var points = new Array(this._geometries.length).fill(null);
99539             for (var i = 0; i < points.length; i++) {
99540               points[i] = this$1._geometries[i].copy();
99541             }
99542             return new MultiPoint(points, this._factory)
99543           };
99544           MultiPoint.prototype.interfaces_ = function interfaces_ () {
99545             return [Puntal]
99546           };
99547           MultiPoint.prototype.getClass = function getClass () {
99548             return MultiPoint
99549           };
99550           staticAccessors.serialVersionUID.get = function () { return -8048474874175355449 };
99551
99552           Object.defineProperties( MultiPoint, staticAccessors );
99553
99554           return MultiPoint;
99555         }(GeometryCollection));
99556
99557         var LinearRing = (function (LineString$$1) {
99558           function LinearRing (points, factory) {
99559             if (points instanceof Coordinate && factory instanceof GeometryFactory) {
99560               points = factory.getCoordinateSequenceFactory().create(points);
99561             }
99562             LineString$$1.call(this, points, factory);
99563             this.validateConstruction();
99564           }
99565
99566           if ( LineString$$1 ) { LinearRing.__proto__ = LineString$$1; }
99567           LinearRing.prototype = Object.create( LineString$$1 && LineString$$1.prototype );
99568           LinearRing.prototype.constructor = LinearRing;
99569
99570           var staticAccessors = { MINIMUM_VALID_SIZE: { configurable: true },serialVersionUID: { configurable: true } };
99571           LinearRing.prototype.getSortIndex = function getSortIndex () {
99572             return Geometry.SORTINDEX_LINEARRING
99573           };
99574           LinearRing.prototype.getBoundaryDimension = function getBoundaryDimension () {
99575             return Dimension.FALSE
99576           };
99577           LinearRing.prototype.isClosed = function isClosed () {
99578             if (this.isEmpty()) {
99579               return true
99580             }
99581             return LineString$$1.prototype.isClosed.call(this)
99582           };
99583           LinearRing.prototype.reverse = function reverse () {
99584             var seq = this._points.copy();
99585             CoordinateSequences.reverse(seq);
99586             var rev = this.getFactory().createLinearRing(seq);
99587             return rev
99588           };
99589           LinearRing.prototype.validateConstruction = function validateConstruction () {
99590             if (!this.isEmpty() && !LineString$$1.prototype.isClosed.call(this)) {
99591               throw new IllegalArgumentException('Points of LinearRing do not form a closed linestring')
99592             }
99593             if (this.getCoordinateSequence().size() >= 1 && this.getCoordinateSequence().size() < LinearRing.MINIMUM_VALID_SIZE) {
99594               throw new IllegalArgumentException('Invalid number of points in LinearRing (found ' + this.getCoordinateSequence().size() + ' - must be 0 or >= 4)')
99595             }
99596           };
99597           LinearRing.prototype.getGeometryType = function getGeometryType () {
99598             return 'LinearRing'
99599           };
99600           LinearRing.prototype.copy = function copy () {
99601             return new LinearRing(this._points.copy(), this._factory)
99602           };
99603           LinearRing.prototype.interfaces_ = function interfaces_ () {
99604             return []
99605           };
99606           LinearRing.prototype.getClass = function getClass () {
99607             return LinearRing
99608           };
99609           staticAccessors.MINIMUM_VALID_SIZE.get = function () { return 4 };
99610           staticAccessors.serialVersionUID.get = function () { return -4261142084085851829 };
99611
99612           Object.defineProperties( LinearRing, staticAccessors );
99613
99614           return LinearRing;
99615         }(LineString));
99616
99617         var MultiPolygon = (function (GeometryCollection$$1) {
99618           function MultiPolygon () {
99619             GeometryCollection$$1.apply(this, arguments);
99620           }
99621
99622           if ( GeometryCollection$$1 ) { MultiPolygon.__proto__ = GeometryCollection$$1; }
99623           MultiPolygon.prototype = Object.create( GeometryCollection$$1 && GeometryCollection$$1.prototype );
99624           MultiPolygon.prototype.constructor = MultiPolygon;
99625
99626           var staticAccessors = { serialVersionUID: { configurable: true } };
99627
99628           MultiPolygon.prototype.getSortIndex = function getSortIndex () {
99629             return Geometry.SORTINDEX_MULTIPOLYGON
99630           };
99631           MultiPolygon.prototype.equalsExact = function equalsExact () {
99632             if (arguments.length === 2) {
99633               var other = arguments[0];
99634               var tolerance = arguments[1];
99635               if (!this.isEquivalentClass(other)) {
99636                 return false
99637               }
99638               return GeometryCollection$$1.prototype.equalsExact.call(this, other, tolerance)
99639             } else { return GeometryCollection$$1.prototype.equalsExact.apply(this, arguments) }
99640           };
99641           MultiPolygon.prototype.getBoundaryDimension = function getBoundaryDimension () {
99642             return 1
99643           };
99644           MultiPolygon.prototype.getDimension = function getDimension () {
99645             return 2
99646           };
99647           MultiPolygon.prototype.reverse = function reverse () {
99648             var this$1 = this;
99649
99650             var n = this._geometries.length;
99651             var revGeoms = new Array(n).fill(null);
99652             for (var i = 0; i < this._geometries.length; i++) {
99653               revGeoms[i] = this$1._geometries[i].reverse();
99654             }
99655             return this.getFactory().createMultiPolygon(revGeoms)
99656           };
99657           MultiPolygon.prototype.getBoundary = function getBoundary () {
99658             var this$1 = this;
99659
99660             if (this.isEmpty()) {
99661               return this.getFactory().createMultiLineString()
99662             }
99663             var allRings = new ArrayList();
99664             for (var i = 0; i < this._geometries.length; i++) {
99665               var polygon = this$1._geometries[i];
99666               var rings = polygon.getBoundary();
99667               for (var j = 0; j < rings.getNumGeometries(); j++) {
99668                 allRings.add(rings.getGeometryN(j));
99669               }
99670             }
99671             var allRingsArray = new Array(allRings.size()).fill(null);
99672             return this.getFactory().createMultiLineString(allRings.toArray(allRingsArray))
99673           };
99674           MultiPolygon.prototype.getGeometryType = function getGeometryType () {
99675             return 'MultiPolygon'
99676           };
99677           MultiPolygon.prototype.copy = function copy () {
99678             var this$1 = this;
99679
99680             var polygons = new Array(this._geometries.length).fill(null);
99681             for (var i = 0; i < polygons.length; i++) {
99682               polygons[i] = this$1._geometries[i].copy();
99683             }
99684             return new MultiPolygon(polygons, this._factory)
99685           };
99686           MultiPolygon.prototype.interfaces_ = function interfaces_ () {
99687             return [Polygonal]
99688           };
99689           MultiPolygon.prototype.getClass = function getClass () {
99690             return MultiPolygon
99691           };
99692           staticAccessors.serialVersionUID.get = function () { return -551033529766975875 };
99693
99694           Object.defineProperties( MultiPolygon, staticAccessors );
99695
99696           return MultiPolygon;
99697         }(GeometryCollection));
99698
99699         var GeometryEditor = function GeometryEditor (factory) {
99700           this._factory = factory || null;
99701           this._isUserDataCopied = false;
99702         };
99703
99704         var staticAccessors$16 = { NoOpGeometryOperation: { configurable: true },CoordinateOperation: { configurable: true },CoordinateSequenceOperation: { configurable: true } };
99705         GeometryEditor.prototype.setCopyUserData = function setCopyUserData (isUserDataCopied) {
99706           this._isUserDataCopied = isUserDataCopied;
99707         };
99708         GeometryEditor.prototype.edit = function edit (geometry, operation) {
99709           if (geometry === null) { return null }
99710           var result = this.editInternal(geometry, operation);
99711           if (this._isUserDataCopied) {
99712             result.setUserData(geometry.getUserData());
99713           }
99714           return result
99715         };
99716         GeometryEditor.prototype.editInternal = function editInternal (geometry, operation) {
99717           if (this._factory === null) { this._factory = geometry.getFactory(); }
99718           if (geometry instanceof GeometryCollection) {
99719             return this.editGeometryCollection(geometry, operation)
99720           }
99721           if (geometry instanceof Polygon) {
99722             return this.editPolygon(geometry, operation)
99723           }
99724           if (geometry instanceof Point$1) {
99725             return operation.edit(geometry, this._factory)
99726           }
99727           if (geometry instanceof LineString) {
99728             return operation.edit(geometry, this._factory)
99729           }
99730           Assert.shouldNeverReachHere('Unsupported Geometry class: ' + geometry.getClass().getName());
99731           return null
99732         };
99733         GeometryEditor.prototype.editGeometryCollection = function editGeometryCollection (collection, operation) {
99734             var this$1 = this;
99735
99736           var collectionForType = operation.edit(collection, this._factory);
99737           var geometries = new ArrayList();
99738           for (var i = 0; i < collectionForType.getNumGeometries(); i++) {
99739             var geometry = this$1.edit(collectionForType.getGeometryN(i), operation);
99740             if (geometry === null || geometry.isEmpty()) {
99741               continue
99742             }
99743             geometries.add(geometry);
99744           }
99745           if (collectionForType.getClass() === MultiPoint) {
99746             return this._factory.createMultiPoint(geometries.toArray([]))
99747           }
99748           if (collectionForType.getClass() === MultiLineString) {
99749             return this._factory.createMultiLineString(geometries.toArray([]))
99750           }
99751           if (collectionForType.getClass() === MultiPolygon) {
99752             return this._factory.createMultiPolygon(geometries.toArray([]))
99753           }
99754           return this._factory.createGeometryCollection(geometries.toArray([]))
99755         };
99756         GeometryEditor.prototype.editPolygon = function editPolygon (polygon, operation) {
99757             var this$1 = this;
99758
99759           var newPolygon = operation.edit(polygon, this._factory);
99760           if (newPolygon === null) { newPolygon = this._factory.createPolygon(null); }
99761           if (newPolygon.isEmpty()) {
99762             return newPolygon
99763           }
99764           var shell = this.edit(newPolygon.getExteriorRing(), operation);
99765           if (shell === null || shell.isEmpty()) {
99766             return this._factory.createPolygon()
99767           }
99768           var holes = new ArrayList();
99769           for (var i = 0; i < newPolygon.getNumInteriorRing(); i++) {
99770             var hole = this$1.edit(newPolygon.getInteriorRingN(i), operation);
99771             if (hole === null || hole.isEmpty()) {
99772               continue
99773             }
99774             holes.add(hole);
99775           }
99776           return this._factory.createPolygon(shell, holes.toArray([]))
99777         };
99778         GeometryEditor.prototype.interfaces_ = function interfaces_ () {
99779           return []
99780         };
99781         GeometryEditor.prototype.getClass = function getClass () {
99782           return GeometryEditor
99783         };
99784         GeometryEditor.GeometryEditorOperation = function GeometryEditorOperation () {};
99785         staticAccessors$16.NoOpGeometryOperation.get = function () { return NoOpGeometryOperation };
99786         staticAccessors$16.CoordinateOperation.get = function () { return CoordinateOperation };
99787         staticAccessors$16.CoordinateSequenceOperation.get = function () { return CoordinateSequenceOperation };
99788
99789         Object.defineProperties( GeometryEditor, staticAccessors$16 );
99790
99791         var NoOpGeometryOperation = function NoOpGeometryOperation () {};
99792
99793         NoOpGeometryOperation.prototype.edit = function edit (geometry, factory) {
99794           return geometry
99795         };
99796         NoOpGeometryOperation.prototype.interfaces_ = function interfaces_ () {
99797           return [GeometryEditor.GeometryEditorOperation]
99798         };
99799         NoOpGeometryOperation.prototype.getClass = function getClass () {
99800           return NoOpGeometryOperation
99801         };
99802
99803         var CoordinateOperation = function CoordinateOperation () {};
99804
99805         CoordinateOperation.prototype.edit = function edit (geometry, factory) {
99806           var coords = this.editCoordinates(geometry.getCoordinates(), geometry);
99807           if (coords === null) { return geometry }
99808           if (geometry instanceof LinearRing) {
99809             return factory.createLinearRing(coords)
99810           }
99811           if (geometry instanceof LineString) {
99812             return factory.createLineString(coords)
99813           }
99814           if (geometry instanceof Point$1) {
99815             if (coords.length > 0) {
99816               return factory.createPoint(coords[0])
99817             } else {
99818               return factory.createPoint()
99819             }
99820           }
99821           return geometry
99822         };
99823         CoordinateOperation.prototype.interfaces_ = function interfaces_ () {
99824           return [GeometryEditor.GeometryEditorOperation]
99825         };
99826         CoordinateOperation.prototype.getClass = function getClass () {
99827           return CoordinateOperation
99828         };
99829
99830         var CoordinateSequenceOperation = function CoordinateSequenceOperation () {};
99831
99832         CoordinateSequenceOperation.prototype.edit = function edit (geometry, factory) {
99833           if (geometry instanceof LinearRing) {
99834             return factory.createLinearRing(this.edit(geometry.getCoordinateSequence(), geometry))
99835           }
99836           if (geometry instanceof LineString) {
99837             return factory.createLineString(this.edit(geometry.getCoordinateSequence(), geometry))
99838           }
99839           if (geometry instanceof Point$1) {
99840             return factory.createPoint(this.edit(geometry.getCoordinateSequence(), geometry))
99841           }
99842           return geometry
99843         };
99844         CoordinateSequenceOperation.prototype.interfaces_ = function interfaces_ () {
99845           return [GeometryEditor.GeometryEditorOperation]
99846         };
99847         CoordinateSequenceOperation.prototype.getClass = function getClass () {
99848           return CoordinateSequenceOperation
99849         };
99850
99851         var CoordinateArraySequence = function CoordinateArraySequence () {
99852           var this$1 = this;
99853
99854           this._dimension = 3;
99855           this._coordinates = null;
99856           if (arguments.length === 1) {
99857             if (arguments[0] instanceof Array) {
99858               this._coordinates = arguments[0];
99859               this._dimension = 3;
99860             } else if (Number.isInteger(arguments[0])) {
99861               var size = arguments[0];
99862               this._coordinates = new Array(size).fill(null);
99863               for (var i = 0; i < size; i++) {
99864                 this$1._coordinates[i] = new Coordinate();
99865               }
99866             } else if (hasInterface(arguments[0], CoordinateSequence)) {
99867               var coordSeq = arguments[0];
99868               if (coordSeq === null) {
99869                 this._coordinates = new Array(0).fill(null);
99870                 return null
99871               }
99872               this._dimension = coordSeq.getDimension();
99873               this._coordinates = new Array(coordSeq.size()).fill(null);
99874               for (var i$1 = 0; i$1 < this._coordinates.length; i$1++) {
99875                 this$1._coordinates[i$1] = coordSeq.getCoordinateCopy(i$1);
99876               }
99877             }
99878           } else if (arguments.length === 2) {
99879             if (arguments[0] instanceof Array && Number.isInteger(arguments[1])) {
99880               var coordinates = arguments[0];
99881               var dimension = arguments[1];
99882               this._coordinates = coordinates;
99883               this._dimension = dimension;
99884               if (coordinates === null) { this._coordinates = new Array(0).fill(null); }
99885             } else if (Number.isInteger(arguments[0]) && Number.isInteger(arguments[1])) {
99886               var size$1 = arguments[0];
99887               var dimension$1 = arguments[1];
99888               this._coordinates = new Array(size$1).fill(null);
99889               this._dimension = dimension$1;
99890               for (var i$2 = 0; i$2 < size$1; i$2++) {
99891                 this$1._coordinates[i$2] = new Coordinate();
99892               }
99893             }
99894           }
99895         };
99896
99897         var staticAccessors$18 = { serialVersionUID: { configurable: true } };
99898         CoordinateArraySequence.prototype.setOrdinate = function setOrdinate (index, ordinateIndex, value) {
99899           switch (ordinateIndex) {
99900             case CoordinateSequence.X:
99901               this._coordinates[index].x = value;
99902               break
99903             case CoordinateSequence.Y:
99904               this._coordinates[index].y = value;
99905               break
99906             case CoordinateSequence.Z:
99907               this._coordinates[index].z = value;
99908               break
99909             default:
99910               throw new IllegalArgumentException('invalid ordinateIndex')
99911           }
99912         };
99913         CoordinateArraySequence.prototype.size = function size () {
99914           return this._coordinates.length
99915         };
99916         CoordinateArraySequence.prototype.getOrdinate = function getOrdinate (index, ordinateIndex) {
99917           switch (ordinateIndex) {
99918             case CoordinateSequence.X:
99919               return this._coordinates[index].x
99920             case CoordinateSequence.Y:
99921               return this._coordinates[index].y
99922             case CoordinateSequence.Z:
99923               return this._coordinates[index].z
99924           }
99925           return Double.NaN
99926         };
99927         CoordinateArraySequence.prototype.getCoordinate = function getCoordinate () {
99928           if (arguments.length === 1) {
99929             var i = arguments[0];
99930             return this._coordinates[i]
99931           } else if (arguments.length === 2) {
99932             var index = arguments[0];
99933             var coord = arguments[1];
99934             coord.x = this._coordinates[index].x;
99935             coord.y = this._coordinates[index].y;
99936             coord.z = this._coordinates[index].z;
99937           }
99938         };
99939         CoordinateArraySequence.prototype.getCoordinateCopy = function getCoordinateCopy (i) {
99940           return new Coordinate(this._coordinates[i])
99941         };
99942         CoordinateArraySequence.prototype.getDimension = function getDimension () {
99943           return this._dimension
99944         };
99945         CoordinateArraySequence.prototype.getX = function getX (index) {
99946           return this._coordinates[index].x
99947         };
99948         CoordinateArraySequence.prototype.clone = function clone () {
99949             var this$1 = this;
99950
99951           var cloneCoordinates = new Array(this.size()).fill(null);
99952           for (var i = 0; i < this._coordinates.length; i++) {
99953             cloneCoordinates[i] = this$1._coordinates[i].clone();
99954           }
99955           return new CoordinateArraySequence(cloneCoordinates, this._dimension)
99956         };
99957         CoordinateArraySequence.prototype.expandEnvelope = function expandEnvelope (env) {
99958             var this$1 = this;
99959
99960           for (var i = 0; i < this._coordinates.length; i++) {
99961             env.expandToInclude(this$1._coordinates[i]);
99962           }
99963           return env
99964         };
99965         CoordinateArraySequence.prototype.copy = function copy () {
99966             var this$1 = this;
99967
99968           var cloneCoordinates = new Array(this.size()).fill(null);
99969           for (var i = 0; i < this._coordinates.length; i++) {
99970             cloneCoordinates[i] = this$1._coordinates[i].copy();
99971           }
99972           return new CoordinateArraySequence(cloneCoordinates, this._dimension)
99973         };
99974         CoordinateArraySequence.prototype.toString = function toString () {
99975             var this$1 = this;
99976
99977           if (this._coordinates.length > 0) {
99978             var strBuf = new StringBuffer(17 * this._coordinates.length);
99979             strBuf.append('(');
99980             strBuf.append(this._coordinates[0]);
99981             for (var i = 1; i < this._coordinates.length; i++) {
99982               strBuf.append(', ');
99983               strBuf.append(this$1._coordinates[i]);
99984             }
99985             strBuf.append(')');
99986             return strBuf.toString()
99987           } else {
99988             return '()'
99989           }
99990         };
99991         CoordinateArraySequence.prototype.getY = function getY (index) {
99992           return this._coordinates[index].y
99993         };
99994         CoordinateArraySequence.prototype.toCoordinateArray = function toCoordinateArray () {
99995           return this._coordinates
99996         };
99997         CoordinateArraySequence.prototype.interfaces_ = function interfaces_ () {
99998           return [CoordinateSequence, Serializable]
99999         };
100000         CoordinateArraySequence.prototype.getClass = function getClass () {
100001           return CoordinateArraySequence
100002         };
100003         staticAccessors$18.serialVersionUID.get = function () { return -915438501601840650 };
100004
100005         Object.defineProperties( CoordinateArraySequence, staticAccessors$18 );
100006
100007         var CoordinateArraySequenceFactory = function CoordinateArraySequenceFactory () {};
100008
100009         var staticAccessors$17 = { serialVersionUID: { configurable: true },instanceObject: { configurable: true } };
100010
100011         CoordinateArraySequenceFactory.prototype.readResolve = function readResolve () {
100012           return CoordinateArraySequenceFactory.instance()
100013         };
100014         CoordinateArraySequenceFactory.prototype.create = function create () {
100015           if (arguments.length === 1) {
100016             if (arguments[0] instanceof Array) {
100017               var coordinates = arguments[0];
100018               return new CoordinateArraySequence(coordinates)
100019             } else if (hasInterface(arguments[0], CoordinateSequence)) {
100020               var coordSeq = arguments[0];
100021               return new CoordinateArraySequence(coordSeq)
100022             }
100023           } else if (arguments.length === 2) {
100024             var size = arguments[0];
100025             var dimension = arguments[1];
100026             if (dimension > 3) { dimension = 3; }
100027             if (dimension < 2) { return new CoordinateArraySequence(size) }
100028             return new CoordinateArraySequence(size, dimension)
100029           }
100030         };
100031         CoordinateArraySequenceFactory.prototype.interfaces_ = function interfaces_ () {
100032           return [CoordinateSequenceFactory, Serializable]
100033         };
100034         CoordinateArraySequenceFactory.prototype.getClass = function getClass () {
100035           return CoordinateArraySequenceFactory
100036         };
100037         CoordinateArraySequenceFactory.instance = function instance () {
100038           return CoordinateArraySequenceFactory.instanceObject
100039         };
100040
100041         staticAccessors$17.serialVersionUID.get = function () { return -4099577099607551657 };
100042         staticAccessors$17.instanceObject.get = function () { return new CoordinateArraySequenceFactory() };
100043
100044         Object.defineProperties( CoordinateArraySequenceFactory, staticAccessors$17 );
100045
100046         /**
100047          * @see http://download.oracle.com/javase/6/docs/api/java/util/HashMap.html
100048          *
100049          * @extends {javascript.util.Map}
100050          * @constructor
100051          * @private
100052          */
100053         var HashMap = (function (MapInterface) {
100054           function HashMap () {
100055             MapInterface.call(this);
100056             this.map_ = new Map();
100057           }
100058
100059           if ( MapInterface ) { HashMap.__proto__ = MapInterface; }
100060           HashMap.prototype = Object.create( MapInterface && MapInterface.prototype );
100061           HashMap.prototype.constructor = HashMap;
100062           /**
100063            * @override
100064            */
100065           HashMap.prototype.get = function get (key) {
100066             return this.map_.get(key) || null
100067           };
100068
100069           /**
100070            * @override
100071            */
100072           HashMap.prototype.put = function put (key, value) {
100073             this.map_.set(key, value);
100074             return value
100075           };
100076
100077           /**
100078            * @override
100079            */
100080           HashMap.prototype.values = function values () {
100081             var arrayList = new ArrayList();
100082             var it = this.map_.values();
100083             var o = it.next();
100084             while (!o.done) {
100085               arrayList.add(o.value);
100086               o = it.next();
100087             }
100088             return arrayList
100089           };
100090
100091           /**
100092            * @override
100093            */
100094           HashMap.prototype.entrySet = function entrySet () {
100095             var hashSet = new HashSet();
100096             this.map_.entries().forEach(function (entry) { return hashSet.add(entry); });
100097             return hashSet
100098           };
100099
100100           /**
100101            * @override
100102            */
100103           HashMap.prototype.size = function size () {
100104             return this.map_.size()
100105           };
100106
100107           return HashMap;
100108         }(Map$1$1));
100109
100110         var PrecisionModel = function PrecisionModel () {
100111           this._modelType = null;
100112           this._scale = null;
100113           if (arguments.length === 0) {
100114             this._modelType = PrecisionModel.FLOATING;
100115           } else if (arguments.length === 1) {
100116             if (arguments[0] instanceof Type$2) {
100117               var modelType = arguments[0];
100118               this._modelType = modelType;
100119               if (modelType === PrecisionModel.FIXED) {
100120                 this.setScale(1.0);
100121               }
100122             } else if (typeof arguments[0] === 'number') {
100123               var scale = arguments[0];
100124               this._modelType = PrecisionModel.FIXED;
100125               this.setScale(scale);
100126             } else if (arguments[0] instanceof PrecisionModel) {
100127               var pm = arguments[0];
100128               this._modelType = pm._modelType;
100129               this._scale = pm._scale;
100130             }
100131           }
100132         };
100133
100134         var staticAccessors$19 = { serialVersionUID: { configurable: true },maximumPreciseValue: { configurable: true } };
100135         PrecisionModel.prototype.equals = function equals (other) {
100136           if (!(other instanceof PrecisionModel)) {
100137             return false
100138           }
100139           var otherPrecisionModel = other;
100140           return this._modelType === otherPrecisionModel._modelType && this._scale === otherPrecisionModel._scale
100141         };
100142         PrecisionModel.prototype.compareTo = function compareTo (o) {
100143           var other = o;
100144           var sigDigits = this.getMaximumSignificantDigits();
100145           var otherSigDigits = other.getMaximumSignificantDigits();
100146           return new Integer(sigDigits).compareTo(new Integer(otherSigDigits))
100147         };
100148         PrecisionModel.prototype.getScale = function getScale () {
100149           return this._scale
100150         };
100151         PrecisionModel.prototype.isFloating = function isFloating () {
100152           return this._modelType === PrecisionModel.FLOATING || this._modelType === PrecisionModel.FLOATING_SINGLE
100153         };
100154         PrecisionModel.prototype.getType = function getType () {
100155           return this._modelType
100156         };
100157         PrecisionModel.prototype.toString = function toString () {
100158           var description = 'UNKNOWN';
100159           if (this._modelType === PrecisionModel.FLOATING) {
100160             description = 'Floating';
100161           } else if (this._modelType === PrecisionModel.FLOATING_SINGLE) {
100162             description = 'Floating-Single';
100163           } else if (this._modelType === PrecisionModel.FIXED) {
100164             description = 'Fixed (Scale=' + this.getScale() + ')';
100165           }
100166           return description
100167         };
100168         PrecisionModel.prototype.makePrecise = function makePrecise () {
100169           if (typeof arguments[0] === 'number') {
100170             var val = arguments[0];
100171             if (Double.isNaN(val)) { return val }
100172             if (this._modelType === PrecisionModel.FLOATING_SINGLE) {
100173               var floatSingleVal = val;
100174               return floatSingleVal
100175             }
100176             if (this._modelType === PrecisionModel.FIXED) {
100177               return Math.round(val * this._scale) / this._scale
100178             }
100179             return val
100180           } else if (arguments[0] instanceof Coordinate) {
100181             var coord = arguments[0];
100182             if (this._modelType === PrecisionModel.FLOATING) { return null }
100183             coord.x = this.makePrecise(coord.x);
100184             coord.y = this.makePrecise(coord.y);
100185           }
100186         };
100187         PrecisionModel.prototype.getMaximumSignificantDigits = function getMaximumSignificantDigits () {
100188           var maxSigDigits = 16;
100189           if (this._modelType === PrecisionModel.FLOATING) {
100190             maxSigDigits = 16;
100191           } else if (this._modelType === PrecisionModel.FLOATING_SINGLE) {
100192             maxSigDigits = 6;
100193           } else if (this._modelType === PrecisionModel.FIXED) {
100194             maxSigDigits = 1 + Math.trunc(Math.ceil(Math.log(this.getScale()) / Math.log(10)));
100195           }
100196           return maxSigDigits
100197         };
100198         PrecisionModel.prototype.setScale = function setScale (scale) {
100199           this._scale = Math.abs(scale);
100200         };
100201         PrecisionModel.prototype.interfaces_ = function interfaces_ () {
100202           return [Serializable, Comparable]
100203         };
100204         PrecisionModel.prototype.getClass = function getClass () {
100205           return PrecisionModel
100206         };
100207         PrecisionModel.mostPrecise = function mostPrecise (pm1, pm2) {
100208           if (pm1.compareTo(pm2) >= 0) { return pm1 }
100209           return pm2
100210         };
100211         staticAccessors$19.serialVersionUID.get = function () { return 7777263578777803835 };
100212         staticAccessors$19.maximumPreciseValue.get = function () { return 9007199254740992.0 };
100213
100214         Object.defineProperties( PrecisionModel, staticAccessors$19 );
100215
100216         var Type$2 = function Type (name) {
100217           this._name = name || null;
100218           Type.nameToTypeMap.put(name, this);
100219         };
100220
100221         var staticAccessors$1$1 = { serialVersionUID: { configurable: true },nameToTypeMap: { configurable: true } };
100222         Type$2.prototype.readResolve = function readResolve () {
100223           return Type$2.nameToTypeMap.get(this._name)
100224         };
100225         Type$2.prototype.toString = function toString () {
100226           return this._name
100227         };
100228         Type$2.prototype.interfaces_ = function interfaces_ () {
100229           return [Serializable]
100230         };
100231         Type$2.prototype.getClass = function getClass () {
100232           return Type$2
100233         };
100234         staticAccessors$1$1.serialVersionUID.get = function () { return -5528602631731589822 };
100235         staticAccessors$1$1.nameToTypeMap.get = function () { return new HashMap() };
100236
100237         Object.defineProperties( Type$2, staticAccessors$1$1 );
100238
100239         PrecisionModel.Type = Type$2;
100240         PrecisionModel.FIXED = new Type$2('FIXED');
100241         PrecisionModel.FLOATING = new Type$2('FLOATING');
100242         PrecisionModel.FLOATING_SINGLE = new Type$2('FLOATING SINGLE');
100243
100244         var GeometryFactory = function GeometryFactory () {
100245           this._precisionModel = new PrecisionModel();
100246           this._SRID = 0;
100247           this._coordinateSequenceFactory = GeometryFactory.getDefaultCoordinateSequenceFactory();
100248
100249           if (arguments.length === 0) ; else if (arguments.length === 1) {
100250             if (hasInterface(arguments[0], CoordinateSequenceFactory)) {
100251               this._coordinateSequenceFactory = arguments[0];
100252             } else if (arguments[0] instanceof PrecisionModel) {
100253               this._precisionModel = arguments[0];
100254             }
100255           } else if (arguments.length === 2) {
100256             this._precisionModel = arguments[0];
100257             this._SRID = arguments[1];
100258           } else if (arguments.length === 3) {
100259             this._precisionModel = arguments[0];
100260             this._SRID = arguments[1];
100261             this._coordinateSequenceFactory = arguments[2];
100262           }
100263         };
100264
100265         var staticAccessors$2 = { serialVersionUID: { configurable: true } };
100266         GeometryFactory.prototype.toGeometry = function toGeometry (envelope) {
100267           if (envelope.isNull()) {
100268             return this.createPoint(null)
100269           }
100270           if (envelope.getMinX() === envelope.getMaxX() && envelope.getMinY() === envelope.getMaxY()) {
100271             return this.createPoint(new Coordinate(envelope.getMinX(), envelope.getMinY()))
100272           }
100273           if (envelope.getMinX() === envelope.getMaxX() || envelope.getMinY() === envelope.getMaxY()) {
100274             return this.createLineString([new Coordinate(envelope.getMinX(), envelope.getMinY()), new Coordinate(envelope.getMaxX(), envelope.getMaxY())])
100275           }
100276           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)
100277         };
100278         GeometryFactory.prototype.createLineString = function createLineString (coordinates) {
100279           if (!coordinates) { return new LineString(this.getCoordinateSequenceFactory().create([]), this) }
100280           else if (coordinates instanceof Array) { return new LineString(this.getCoordinateSequenceFactory().create(coordinates), this) }
100281           else if (hasInterface(coordinates, CoordinateSequence)) { return new LineString(coordinates, this) }
100282         };
100283         GeometryFactory.prototype.createMultiLineString = function createMultiLineString () {
100284           if (arguments.length === 0) {
100285             return new MultiLineString(null, this)
100286           } else if (arguments.length === 1) {
100287             var lineStrings = arguments[0];
100288             return new MultiLineString(lineStrings, this)
100289           }
100290         };
100291         GeometryFactory.prototype.buildGeometry = function buildGeometry (geomList) {
100292           var geomClass = null;
100293           var isHeterogeneous = false;
100294           var hasGeometryCollection = false;
100295           for (var i = geomList.iterator(); i.hasNext();) {
100296             var geom = i.next();
100297             var partClass = geom.getClass();
100298             if (geomClass === null) {
100299               geomClass = partClass;
100300             }
100301             if (partClass !== geomClass) {
100302               isHeterogeneous = true;
100303             }
100304             if (geom.isGeometryCollectionOrDerived()) { hasGeometryCollection = true; }
100305           }
100306           if (geomClass === null) {
100307             return this.createGeometryCollection()
100308           }
100309           if (isHeterogeneous || hasGeometryCollection) {
100310             return this.createGeometryCollection(GeometryFactory.toGeometryArray(geomList))
100311           }
100312           var geom0 = geomList.iterator().next();
100313           var isCollection = geomList.size() > 1;
100314           if (isCollection) {
100315             if (geom0 instanceof Polygon) {
100316               return this.createMultiPolygon(GeometryFactory.toPolygonArray(geomList))
100317             } else if (geom0 instanceof LineString) {
100318               return this.createMultiLineString(GeometryFactory.toLineStringArray(geomList))
100319             } else if (geom0 instanceof Point$1) {
100320               return this.createMultiPoint(GeometryFactory.toPointArray(geomList))
100321             }
100322             Assert.shouldNeverReachHere('Unhandled class: ' + geom0.getClass().getName());
100323           }
100324           return geom0
100325         };
100326         GeometryFactory.prototype.createMultiPointFromCoords = function createMultiPointFromCoords (coordinates) {
100327           return this.createMultiPoint(coordinates !== null ? this.getCoordinateSequenceFactory().create(coordinates) : null)
100328         };
100329         GeometryFactory.prototype.createPoint = function createPoint () {
100330           if (arguments.length === 0) {
100331             return this.createPoint(this.getCoordinateSequenceFactory().create([]))
100332           } else if (arguments.length === 1) {
100333             if (arguments[0] instanceof Coordinate) {
100334               var coordinate = arguments[0];
100335               return this.createPoint(coordinate !== null ? this.getCoordinateSequenceFactory().create([coordinate]) : null)
100336             } else if (hasInterface(arguments[0], CoordinateSequence)) {
100337               var coordinates = arguments[0];
100338               return new Point$1(coordinates, this)
100339             }
100340           }
100341         };
100342         GeometryFactory.prototype.getCoordinateSequenceFactory = function getCoordinateSequenceFactory () {
100343           return this._coordinateSequenceFactory
100344         };
100345         GeometryFactory.prototype.createPolygon = function createPolygon () {
100346           if (arguments.length === 0) {
100347             return new Polygon(null, null, this)
100348           } else if (arguments.length === 1) {
100349             if (hasInterface(arguments[0], CoordinateSequence)) {
100350               var coordinates = arguments[0];
100351               return this.createPolygon(this.createLinearRing(coordinates))
100352             } else if (arguments[0] instanceof Array) {
100353               var coordinates$1 = arguments[0];
100354               return this.createPolygon(this.createLinearRing(coordinates$1))
100355             } else if (arguments[0] instanceof LinearRing) {
100356               var shell = arguments[0];
100357               return this.createPolygon(shell, null)
100358             }
100359           } else if (arguments.length === 2) {
100360             var shell$1 = arguments[0];
100361             var holes = arguments[1];
100362             return new Polygon(shell$1, holes, this)
100363           }
100364         };
100365         GeometryFactory.prototype.getSRID = function getSRID () {
100366           return this._SRID
100367         };
100368         GeometryFactory.prototype.createGeometryCollection = function createGeometryCollection () {
100369           if (arguments.length === 0) {
100370             return new GeometryCollection(null, this)
100371           } else if (arguments.length === 1) {
100372             var geometries = arguments[0];
100373             return new GeometryCollection(geometries, this)
100374           }
100375         };
100376         GeometryFactory.prototype.createGeometry = function createGeometry (g) {
100377           var editor = new GeometryEditor(this);
100378           return editor.edit(g, {
100379             edit: function () {
100380               if (arguments.length === 2) {
100381                 var coordSeq = arguments[0];
100382                 // const geometry = arguments[1]
100383                 return this._coordinateSequenceFactory.create(coordSeq)
100384               }
100385             }
100386           })
100387         };
100388         GeometryFactory.prototype.getPrecisionModel = function getPrecisionModel () {
100389           return this._precisionModel
100390         };
100391         GeometryFactory.prototype.createLinearRing = function createLinearRing () {
100392           if (arguments.length === 0) {
100393             return this.createLinearRing(this.getCoordinateSequenceFactory().create([]))
100394           } else if (arguments.length === 1) {
100395             if (arguments[0] instanceof Array) {
100396               var coordinates = arguments[0];
100397               return this.createLinearRing(coordinates !== null ? this.getCoordinateSequenceFactory().create(coordinates) : null)
100398             } else if (hasInterface(arguments[0], CoordinateSequence)) {
100399               var coordinates$1 = arguments[0];
100400               return new LinearRing(coordinates$1, this)
100401             }
100402           }
100403         };
100404         GeometryFactory.prototype.createMultiPolygon = function createMultiPolygon () {
100405           if (arguments.length === 0) {
100406             return new MultiPolygon(null, this)
100407           } else if (arguments.length === 1) {
100408             var polygons = arguments[0];
100409             return new MultiPolygon(polygons, this)
100410           }
100411         };
100412         GeometryFactory.prototype.createMultiPoint = function createMultiPoint () {
100413             var this$1 = this;
100414
100415           if (arguments.length === 0) {
100416             return new MultiPoint(null, this)
100417           } else if (arguments.length === 1) {
100418             if (arguments[0] instanceof Array) {
100419               var point = arguments[0];
100420               return new MultiPoint(point, this)
100421             } else if (arguments[0] instanceof Array) {
100422               var coordinates = arguments[0];
100423               return this.createMultiPoint(coordinates !== null ? this.getCoordinateSequenceFactory().create(coordinates) : null)
100424             } else if (hasInterface(arguments[0], CoordinateSequence)) {
100425               var coordinates$1 = arguments[0];
100426               if (coordinates$1 === null) {
100427                 return this.createMultiPoint(new Array(0).fill(null))
100428               }
100429               var points = new Array(coordinates$1.size()).fill(null);
100430               for (var i = 0; i < coordinates$1.size(); i++) {
100431                 var ptSeq = this$1.getCoordinateSequenceFactory().create(1, coordinates$1.getDimension());
100432                 CoordinateSequences.copy(coordinates$1, i, ptSeq, 0, 1);
100433                 points[i] = this$1.createPoint(ptSeq);
100434               }
100435               return this.createMultiPoint(points)
100436             }
100437           }
100438         };
100439         GeometryFactory.prototype.interfaces_ = function interfaces_ () {
100440           return [Serializable]
100441         };
100442         GeometryFactory.prototype.getClass = function getClass () {
100443           return GeometryFactory
100444         };
100445         GeometryFactory.toMultiPolygonArray = function toMultiPolygonArray (multiPolygons) {
100446           var multiPolygonArray = new Array(multiPolygons.size()).fill(null);
100447           return multiPolygons.toArray(multiPolygonArray)
100448         };
100449         GeometryFactory.toGeometryArray = function toGeometryArray (geometries) {
100450           if (geometries === null) { return null }
100451           var geometryArray = new Array(geometries.size()).fill(null);
100452           return geometries.toArray(geometryArray)
100453         };
100454         GeometryFactory.getDefaultCoordinateSequenceFactory = function getDefaultCoordinateSequenceFactory () {
100455           return CoordinateArraySequenceFactory.instance()
100456         };
100457         GeometryFactory.toMultiLineStringArray = function toMultiLineStringArray (multiLineStrings) {
100458           var multiLineStringArray = new Array(multiLineStrings.size()).fill(null);
100459           return multiLineStrings.toArray(multiLineStringArray)
100460         };
100461         GeometryFactory.toLineStringArray = function toLineStringArray (lineStrings) {
100462           var lineStringArray = new Array(lineStrings.size()).fill(null);
100463           return lineStrings.toArray(lineStringArray)
100464         };
100465         GeometryFactory.toMultiPointArray = function toMultiPointArray (multiPoints) {
100466           var multiPointArray = new Array(multiPoints.size()).fill(null);
100467           return multiPoints.toArray(multiPointArray)
100468         };
100469         GeometryFactory.toLinearRingArray = function toLinearRingArray (linearRings) {
100470           var linearRingArray = new Array(linearRings.size()).fill(null);
100471           return linearRings.toArray(linearRingArray)
100472         };
100473         GeometryFactory.toPointArray = function toPointArray (points) {
100474           var pointArray = new Array(points.size()).fill(null);
100475           return points.toArray(pointArray)
100476         };
100477         GeometryFactory.toPolygonArray = function toPolygonArray (polygons) {
100478           var polygonArray = new Array(polygons.size()).fill(null);
100479           return polygons.toArray(polygonArray)
100480         };
100481         GeometryFactory.createPointFromInternalCoord = function createPointFromInternalCoord (coord, exemplar) {
100482           exemplar.getPrecisionModel().makePrecise(coord);
100483           return exemplar.getFactory().createPoint(coord)
100484         };
100485         staticAccessors$2.serialVersionUID.get = function () { return -6820524753094095635 };
100486
100487         Object.defineProperties( GeometryFactory, staticAccessors$2 );
100488
100489         var geometryTypes = ['Point', 'MultiPoint', 'LineString', 'MultiLineString', 'Polygon', 'MultiPolygon'];
100490
100491         /**
100492          * Class for reading and writing Well-Known Text.Create a new parser for GeoJSON
100493          * NOTE: Adapted from OpenLayers 2.11 implementation.
100494          */
100495
100496         /**
100497          * Create a new parser for GeoJSON
100498          *
100499          * @param {GeometryFactory} geometryFactory
100500          * @return An instance of GeoJsonParser.
100501          * @constructor
100502          * @private
100503          */
100504         var GeoJSONParser = function GeoJSONParser (geometryFactory) {
100505           this.geometryFactory = geometryFactory || new GeometryFactory();
100506         };
100507         /**
100508          * Deserialize a GeoJSON object and return the Geometry or Feature(Collection) with JSTS Geometries
100509          *
100510          * @param {}
100511          *        A GeoJSON object.
100512          * @return {} A Geometry instance or object representing a Feature(Collection) with Geometry instances.
100513          * @private
100514          */
100515         GeoJSONParser.prototype.read = function read (json) {
100516           var obj;
100517           if (typeof json === 'string') {
100518             obj = JSON.parse(json);
100519           } else {
100520             obj = json;
100521           }
100522
100523           var type = obj.type;
100524
100525           if (!parse$2[type]) {
100526             throw new Error('Unknown GeoJSON type: ' + obj.type)
100527           }
100528
100529           if (geometryTypes.indexOf(type) !== -1) {
100530             return parse$2[type].apply(this, [obj.coordinates])
100531           } else if (type === 'GeometryCollection') {
100532             return parse$2[type].apply(this, [obj.geometries])
100533           }
100534
100535           // feature or feature collection
100536           return parse$2[type].apply(this, [obj])
100537         };
100538
100539         /**
100540          * Serialize a Geometry object into GeoJSON
100541          *
100542          * @param {Geometry}
100543          *        geometry A Geometry or array of Geometries.
100544          * @return {Object} A GeoJSON object represting the input Geometry/Geometries.
100545          * @private
100546          */
100547         GeoJSONParser.prototype.write = function write (geometry) {
100548           var type = geometry.getGeometryType();
100549
100550           if (!extract[type]) {
100551             throw new Error('Geometry is not supported')
100552           }
100553
100554           return extract[type].apply(this, [geometry])
100555         };
100556
100557         var parse$2 = {
100558           /**
100559            * Parse a GeoJSON Feature object
100560            *
100561            * @param {Object}
100562            *          obj Object to parse.
100563            *
100564            * @return {Object} Feature with geometry/bbox converted to JSTS Geometries.
100565            */
100566           Feature: function (obj) {
100567             var feature = {};
100568
100569             // copy features
100570             for (var key in obj) {
100571               feature[key] = obj[key];
100572             }
100573
100574             // parse geometry
100575             if (obj.geometry) {
100576               var type = obj.geometry.type;
100577               if (!parse$2[type]) {
100578                 throw new Error('Unknown GeoJSON type: ' + obj.type)
100579               }
100580               feature.geometry = this.read(obj.geometry);
100581             }
100582
100583             // bbox
100584             if (obj.bbox) {
100585               feature.bbox = parse$2.bbox.apply(this, [obj.bbox]);
100586             }
100587
100588             return feature
100589           },
100590
100591           /**
100592            * Parse a GeoJSON FeatureCollection object
100593            *
100594            * @param {Object}
100595            *          obj Object to parse.
100596            *
100597            * @return {Object} FeatureCollection with geometry/bbox converted to JSTS Geometries.
100598            */
100599           FeatureCollection: function (obj) {
100600             var this$1 = this;
100601
100602             var featureCollection = {};
100603
100604             if (obj.features) {
100605               featureCollection.features = [];
100606
100607               for (var i = 0; i < obj.features.length; ++i) {
100608                 featureCollection.features.push(this$1.read(obj.features[i]));
100609               }
100610             }
100611
100612             if (obj.bbox) {
100613               featureCollection.bbox = this.parse.bbox.apply(this, [obj.bbox]);
100614             }
100615
100616             return featureCollection
100617           },
100618
100619           /**
100620            * Convert the ordinates in an array to an array of Coordinates
100621            *
100622            * @param {Array}
100623            *          array Array with {Number}s.
100624            *
100625            * @return {Array} Array with Coordinates.
100626            */
100627           coordinates: function (array) {
100628             var coordinates = [];
100629             for (var i = 0; i < array.length; ++i) {
100630               var sub = array[i];
100631               coordinates.push(new Coordinate(sub[0], sub[1]));
100632             }
100633             return coordinates
100634           },
100635
100636           /**
100637            * Convert the bbox to a LinearRing
100638            *
100639            * @param {Array}
100640            *          array Array with [xMin, yMin, xMax, yMax].
100641            *
100642            * @return {Array} Array with Coordinates.
100643            */
100644           bbox: function (array) {
100645             return this.geometryFactory.createLinearRing([
100646               new Coordinate(array[0], array[1]),
100647               new Coordinate(array[2], array[1]),
100648               new Coordinate(array[2], array[3]),
100649               new Coordinate(array[0], array[3]),
100650               new Coordinate(array[0], array[1])
100651             ])
100652           },
100653
100654           /**
100655            * Convert an Array with ordinates to a Point
100656            *
100657            * @param {Array}
100658            *          array Array with ordinates.
100659            *
100660            * @return {Point} Point.
100661            */
100662           Point: function (array) {
100663             var coordinate = new Coordinate(array[0], array[1]);
100664             return this.geometryFactory.createPoint(coordinate)
100665           },
100666
100667           /**
100668            * Convert an Array with coordinates to a MultiPoint
100669            *
100670            * @param {Array}
100671            *          array Array with coordinates.
100672            *
100673            * @return {MultiPoint} MultiPoint.
100674            */
100675           MultiPoint: function (array) {
100676             var this$1 = this;
100677
100678             var points = [];
100679             for (var i = 0; i < array.length; ++i) {
100680               points.push(parse$2.Point.apply(this$1, [array[i]]));
100681             }
100682             return this.geometryFactory.createMultiPoint(points)
100683           },
100684
100685           /**
100686            * Convert an Array with coordinates to a LineString
100687            *
100688            * @param {Array}
100689            *          array Array with coordinates.
100690            *
100691            * @return {LineString} LineString.
100692            */
100693           LineString: function (array) {
100694             var coordinates = parse$2.coordinates.apply(this, [array]);
100695             return this.geometryFactory.createLineString(coordinates)
100696           },
100697
100698           /**
100699            * Convert an Array with coordinates to a MultiLineString
100700            *
100701            * @param {Array}
100702            *          array Array with coordinates.
100703            *
100704            * @return {MultiLineString} MultiLineString.
100705            */
100706           MultiLineString: function (array) {
100707             var this$1 = this;
100708
100709             var lineStrings = [];
100710             for (var i = 0; i < array.length; ++i) {
100711               lineStrings.push(parse$2.LineString.apply(this$1, [array[i]]));
100712             }
100713             return this.geometryFactory.createMultiLineString(lineStrings)
100714           },
100715
100716           /**
100717            * Convert an Array to a Polygon
100718            *
100719            * @param {Array}
100720            *          array Array with shell and holes.
100721            *
100722            * @return {Polygon} Polygon.
100723            */
100724           Polygon: function (array) {
100725             var this$1 = this;
100726
100727             var shellCoordinates = parse$2.coordinates.apply(this, [array[0]]);
100728             var shell = this.geometryFactory.createLinearRing(shellCoordinates);
100729             var holes = [];
100730             for (var i = 1; i < array.length; ++i) {
100731               var hole = array[i];
100732               var coordinates = parse$2.coordinates.apply(this$1, [hole]);
100733               var linearRing = this$1.geometryFactory.createLinearRing(coordinates);
100734               holes.push(linearRing);
100735             }
100736             return this.geometryFactory.createPolygon(shell, holes)
100737           },
100738
100739           /**
100740            * Convert an Array to a MultiPolygon
100741            *
100742            * @param {Array}
100743            *          array Array of arrays with shell and rings.
100744            *
100745            * @return {MultiPolygon} MultiPolygon.
100746            */
100747           MultiPolygon: function (array) {
100748             var this$1 = this;
100749
100750             var polygons = [];
100751             for (var i = 0; i < array.length; ++i) {
100752               var polygon = array[i];
100753               polygons.push(parse$2.Polygon.apply(this$1, [polygon]));
100754             }
100755             return this.geometryFactory.createMultiPolygon(polygons)
100756           },
100757
100758           /**
100759            * Convert an Array to a GeometryCollection
100760            *
100761            * @param {Array}
100762            *          array Array of GeoJSON geometries.
100763            *
100764            * @return {GeometryCollection} GeometryCollection.
100765            */
100766           GeometryCollection: function (array) {
100767             var this$1 = this;
100768
100769             var geometries = [];
100770             for (var i = 0; i < array.length; ++i) {
100771               var geometry = array[i];
100772               geometries.push(this$1.read(geometry));
100773             }
100774             return this.geometryFactory.createGeometryCollection(geometries)
100775           }
100776         };
100777
100778         var extract = {
100779           /**
100780            * Convert a Coordinate to an Array
100781            *
100782            * @param {Coordinate}
100783            *          coordinate Coordinate to convert.
100784            *
100785            * @return {Array} Array of ordinates.
100786            */
100787           coordinate: function (coordinate) {
100788             return [coordinate.x, coordinate.y]
100789           },
100790
100791           /**
100792            * Convert a Point to a GeoJSON object
100793            *
100794            * @param {Point}
100795            *          point Point to convert.
100796            *
100797            * @return {Array} Array of 2 ordinates (paired to a coordinate).
100798            */
100799           Point: function (point) {
100800             var array = extract.coordinate.apply(this, [point.getCoordinate()]);
100801             return {
100802               type: 'Point',
100803               coordinates: array
100804             }
100805           },
100806
100807           /**
100808            * Convert a MultiPoint to a GeoJSON object
100809            *
100810            * @param {MultiPoint}
100811            *          multipoint MultiPoint to convert.
100812            *
100813            * @return {Array} Array of coordinates.
100814            */
100815           MultiPoint: function (multipoint) {
100816             var this$1 = this;
100817
100818             var array = [];
100819             for (var i = 0; i < multipoint._geometries.length; ++i) {
100820               var point = multipoint._geometries[i];
100821               var geoJson = extract.Point.apply(this$1, [point]);
100822               array.push(geoJson.coordinates);
100823             }
100824             return {
100825               type: 'MultiPoint',
100826               coordinates: array
100827             }
100828           },
100829
100830           /**
100831            * Convert a LineString to a GeoJSON object
100832            *
100833            * @param {LineString}
100834            *          linestring LineString to convert.
100835            *
100836            * @return {Array} Array of coordinates.
100837            */
100838           LineString: function (linestring) {
100839             var this$1 = this;
100840
100841             var array = [];
100842             var coordinates = linestring.getCoordinates();
100843             for (var i = 0; i < coordinates.length; ++i) {
100844               var coordinate = coordinates[i];
100845               array.push(extract.coordinate.apply(this$1, [coordinate]));
100846             }
100847             return {
100848               type: 'LineString',
100849               coordinates: array
100850             }
100851           },
100852
100853           /**
100854            * Convert a MultiLineString to a GeoJSON object
100855            *
100856            * @param {MultiLineString}
100857            *          multilinestring MultiLineString to convert.
100858            *
100859            * @return {Array} Array of Array of coordinates.
100860            */
100861           MultiLineString: function (multilinestring) {
100862             var this$1 = this;
100863
100864             var array = [];
100865             for (var i = 0; i < multilinestring._geometries.length; ++i) {
100866               var linestring = multilinestring._geometries[i];
100867               var geoJson = extract.LineString.apply(this$1, [linestring]);
100868               array.push(geoJson.coordinates);
100869             }
100870             return {
100871               type: 'MultiLineString',
100872               coordinates: array
100873             }
100874           },
100875
100876           /**
100877            * Convert a Polygon to a GeoJSON object
100878            *
100879            * @param {Polygon}
100880            *          polygon Polygon to convert.
100881            *
100882            * @return {Array} Array with shell, holes.
100883            */
100884           Polygon: function (polygon) {
100885             var this$1 = this;
100886
100887             var array = [];
100888             var shellGeoJson = extract.LineString.apply(this, [polygon._shell]);
100889             array.push(shellGeoJson.coordinates);
100890             for (var i = 0; i < polygon._holes.length; ++i) {
100891               var hole = polygon._holes[i];
100892               var holeGeoJson = extract.LineString.apply(this$1, [hole]);
100893               array.push(holeGeoJson.coordinates);
100894             }
100895             return {
100896               type: 'Polygon',
100897               coordinates: array
100898             }
100899           },
100900
100901           /**
100902            * Convert a MultiPolygon to a GeoJSON object
100903            *
100904            * @param {MultiPolygon}
100905            *          multipolygon MultiPolygon to convert.
100906            *
100907            * @return {Array} Array of polygons.
100908            */
100909           MultiPolygon: function (multipolygon) {
100910             var this$1 = this;
100911
100912             var array = [];
100913             for (var i = 0; i < multipolygon._geometries.length; ++i) {
100914               var polygon = multipolygon._geometries[i];
100915               var geoJson = extract.Polygon.apply(this$1, [polygon]);
100916               array.push(geoJson.coordinates);
100917             }
100918             return {
100919               type: 'MultiPolygon',
100920               coordinates: array
100921             }
100922           },
100923
100924           /**
100925            * Convert a GeometryCollection to a GeoJSON object
100926            *
100927            * @param {GeometryCollection}
100928            *          collection GeometryCollection to convert.
100929            *
100930            * @return {Array} Array of geometries.
100931            */
100932           GeometryCollection: function (collection) {
100933             var this$1 = this;
100934
100935             var array = [];
100936             for (var i = 0; i < collection._geometries.length; ++i) {
100937               var geometry = collection._geometries[i];
100938               var type = geometry.getGeometryType();
100939               array.push(extract[type].apply(this$1, [geometry]));
100940             }
100941             return {
100942               type: 'GeometryCollection',
100943               geometries: array
100944             }
100945           }
100946         };
100947
100948         /**
100949          * Converts a geometry in GeoJSON to a {@link Geometry}.
100950          */
100951
100952         /**
100953          * A <code>GeoJSONReader</code> is parameterized by a <code>GeometryFactory</code>,
100954          * to allow it to create <code>Geometry</code> objects of the appropriate
100955          * implementation. In particular, the <code>GeometryFactory</code> determines
100956          * the <code>PrecisionModel</code> and <code>SRID</code> that is used.
100957          *
100958          * @param {GeometryFactory} geometryFactory
100959          * @constructor
100960          */
100961         var GeoJSONReader = function GeoJSONReader (geometryFactory) {
100962           this.geometryFactory = geometryFactory || new GeometryFactory();
100963           this.precisionModel = this.geometryFactory.getPrecisionModel();
100964           this.parser = new GeoJSONParser(this.geometryFactory);
100965         };
100966         /**
100967          * Reads a GeoJSON representation of a {@link Geometry}
100968          *
100969          * Will also parse GeoJSON Features/FeatureCollections as custom objects.
100970          *
100971          * @param {Object|String} geoJson a GeoJSON Object or String.
100972          * @return {Geometry|Object} a <code>Geometry or Feature/FeatureCollection representation.</code>
100973          * @memberof GeoJSONReader
100974          */
100975         GeoJSONReader.prototype.read = function read (geoJson) {
100976           var geometry = this.parser.read(geoJson);
100977
100978           if (this.precisionModel.getType() === PrecisionModel.FIXED) {
100979             this.reducePrecision(geometry);
100980           }
100981
100982           return geometry
100983         };
100984
100985         // NOTE: this is a hack
100986         GeoJSONReader.prototype.reducePrecision = function reducePrecision (geometry) {
100987             var this$1 = this;
100988
100989           var i, len;
100990
100991           if (geometry.coordinate) {
100992             this.precisionModel.makePrecise(geometry.coordinate);
100993           } else if (geometry.points) {
100994             for (i = 0, len = geometry.points.length; i < len; i++) {
100995               this$1.precisionModel.makePrecise(geometry.points[i]);
100996             }
100997           } else if (geometry.geometries) {
100998             for (i = 0, len = geometry.geometries.length; i < len; i++) {
100999               this$1.reducePrecision(geometry.geometries[i]);
101000             }
101001           }
101002         };
101003
101004         /**
101005          * @module GeoJSONWriter
101006          */
101007
101008         /**
101009          * Writes the GeoJSON representation of a {@link Geometry}. The
101010          * The GeoJSON format is defined <A
101011          * HREF="http://geojson.org/geojson-spec.html">here</A>.
101012          */
101013
101014         /**
101015          * The <code>GeoJSONWriter</code> outputs coordinates rounded to the precision
101016          * model. Only the maximum number of decimal places necessary to represent the
101017          * ordinates to the required precision will be output.
101018          *
101019          * @param {GeometryFactory} geometryFactory
101020          * @constructor
101021          */
101022         var GeoJSONWriter = function GeoJSONWriter () {
101023           this.parser = new GeoJSONParser(this.geometryFactory);
101024         };
101025         /**
101026          * Converts a <code>Geometry</code> to its GeoJSON representation.
101027          *
101028          * @param {Geometry}
101029          *        geometry a <code>Geometry</code> to process.
101030          * @return {Object} The GeoJSON representation of the Geometry.
101031          * @memberof GeoJSONWriter
101032          */
101033         GeoJSONWriter.prototype.write = function write (geometry) {
101034           return this.parser.write(geometry)
101035         };
101036
101037         /* eslint-disable no-undef */
101038
101039         // io
101040
101041         var Position = function Position () {};
101042
101043         var staticAccessors$20 = { ON: { configurable: true },LEFT: { configurable: true },RIGHT: { configurable: true } };
101044
101045         Position.prototype.interfaces_ = function interfaces_ () {
101046           return []
101047         };
101048         Position.prototype.getClass = function getClass () {
101049           return Position
101050         };
101051         Position.opposite = function opposite (position) {
101052           if (position === Position.LEFT) { return Position.RIGHT }
101053           if (position === Position.RIGHT) { return Position.LEFT }
101054           return position
101055         };
101056         staticAccessors$20.ON.get = function () { return 0 };
101057         staticAccessors$20.LEFT.get = function () { return 1 };
101058         staticAccessors$20.RIGHT.get = function () { return 2 };
101059
101060         Object.defineProperties( Position, staticAccessors$20 );
101061
101062         /**
101063          * @param {string=} message Optional message
101064          * @extends {Error}
101065          * @constructor
101066          * @private
101067          */
101068         function EmptyStackException (message) {
101069           this.message = message || '';
101070         }
101071         EmptyStackException.prototype = new Error();
101072
101073         /**
101074          * @type {string}
101075          */
101076         EmptyStackException.prototype.name = 'EmptyStackException';
101077
101078         /**
101079          * @see http://download.oracle.com/javase/6/docs/api/java/util/Stack.html
101080          *
101081          * @extends {List}
101082          * @constructor
101083          * @private
101084          */
101085         function Stack () {
101086           /**
101087            * @type {Array}
101088            * @private
101089            */
101090           this.array_ = [];
101091         }
101092         Stack.prototype = new List();
101093
101094         /**
101095          * @override
101096          */
101097         Stack.prototype.add = function (e) {
101098           this.array_.push(e);
101099           return true
101100         };
101101
101102         /**
101103          * @override
101104          */
101105         Stack.prototype.get = function (index) {
101106           if (index < 0 || index >= this.size()) {
101107             throw new Error()
101108           }
101109
101110           return this.array_[index]
101111         };
101112
101113         /**
101114          * Pushes an item onto the top of this stack.
101115          * @param {Object} e
101116          * @return {Object}
101117          */
101118         Stack.prototype.push = function (e) {
101119           this.array_.push(e);
101120           return e
101121         };
101122
101123         /**
101124          * Pushes an item onto the top of this stack.
101125          * @param {Object} e
101126          * @return {Object}
101127          */
101128         Stack.prototype.pop = function (e) {
101129           if (this.array_.length === 0) {
101130             throw new EmptyStackException()
101131           }
101132
101133           return this.array_.pop()
101134         };
101135
101136         /**
101137          * Looks at the object at the top of this stack without removing it from the
101138          * stack.
101139          * @return {Object}
101140          */
101141         Stack.prototype.peek = function () {
101142           if (this.array_.length === 0) {
101143             throw new EmptyStackException()
101144           }
101145
101146           return this.array_[this.array_.length - 1]
101147         };
101148
101149         /**
101150          * Tests if this stack is empty.
101151          * @return {boolean} true if and only if this stack contains no items; false
101152          *         otherwise.
101153          */
101154         Stack.prototype.empty = function () {
101155           if (this.array_.length === 0) {
101156             return true
101157           } else {
101158             return false
101159           }
101160         };
101161
101162         /**
101163          * @return {boolean}
101164          */
101165         Stack.prototype.isEmpty = function () {
101166           return this.empty()
101167         };
101168
101169         /**
101170          * Returns the 1-based position where an object is on this stack. If the object
101171          * o occurs as an item in this stack, this method returns the distance from the
101172          * top of the stack of the occurrence nearest the top of the stack; the topmost
101173          * item on the stack is considered to be at distance 1. The equals method is
101174          * used to compare o to the items in this stack.
101175          *
101176          * NOTE: does not currently actually use equals. (=== is used)
101177          *
101178          * @param {Object} o
101179          * @return {number} the 1-based position from the top of the stack where the
101180          *         object is located; the return value -1 indicates that the object is
101181          *         not on the stack.
101182          */
101183         Stack.prototype.search = function (o) {
101184           return this.array_.indexOf(o)
101185         };
101186
101187         /**
101188          * @return {number}
101189          * @export
101190          */
101191         Stack.prototype.size = function () {
101192           return this.array_.length
101193         };
101194
101195         /**
101196          * @return {Array}
101197          */
101198         Stack.prototype.toArray = function () {
101199           var this$1 = this;
101200
101201           var array = [];
101202
101203           for (var i = 0, len = this.array_.length; i < len; i++) {
101204             array.push(this$1.array_[i]);
101205           }
101206
101207           return array
101208         };
101209
101210         var RightmostEdgeFinder = function RightmostEdgeFinder () {
101211           this._minIndex = -1;
101212           this._minCoord = null;
101213           this._minDe = null;
101214           this._orientedDe = null;
101215         };
101216         RightmostEdgeFinder.prototype.getCoordinate = function getCoordinate () {
101217           return this._minCoord
101218         };
101219         RightmostEdgeFinder.prototype.getRightmostSide = function getRightmostSide (de, index) {
101220           var side = this.getRightmostSideOfSegment(de, index);
101221           if (side < 0) { side = this.getRightmostSideOfSegment(de, index - 1); }
101222           if (side < 0) {
101223             this._minCoord = null;
101224             this.checkForRightmostCoordinate(de);
101225           }
101226           return side
101227         };
101228         RightmostEdgeFinder.prototype.findRightmostEdgeAtVertex = function findRightmostEdgeAtVertex () {
101229           var pts = this._minDe.getEdge().getCoordinates();
101230           Assert.isTrue(this._minIndex > 0 && this._minIndex < pts.length, 'rightmost point expected to be interior vertex of edge');
101231           var pPrev = pts[this._minIndex - 1];
101232           var pNext = pts[this._minIndex + 1];
101233           var orientation = CGAlgorithms.computeOrientation(this._minCoord, pNext, pPrev);
101234           var usePrev = false;
101235           if (pPrev.y < this._minCoord.y && pNext.y < this._minCoord.y && orientation === CGAlgorithms.COUNTERCLOCKWISE) {
101236             usePrev = true;
101237           } else if (pPrev.y > this._minCoord.y && pNext.y > this._minCoord.y && orientation === CGAlgorithms.CLOCKWISE) {
101238             usePrev = true;
101239           }
101240           if (usePrev) {
101241             this._minIndex = this._minIndex - 1;
101242           }
101243         };
101244         RightmostEdgeFinder.prototype.getRightmostSideOfSegment = function getRightmostSideOfSegment (de, i) {
101245           var e = de.getEdge();
101246           var coord = e.getCoordinates();
101247           if (i < 0 || i + 1 >= coord.length) { return -1 }
101248           if (coord[i].y === coord[i + 1].y) { return -1 }
101249           var pos = Position.LEFT;
101250           if (coord[i].y < coord[i + 1].y) { pos = Position.RIGHT; }
101251           return pos
101252         };
101253         RightmostEdgeFinder.prototype.getEdge = function getEdge () {
101254           return this._orientedDe
101255         };
101256         RightmostEdgeFinder.prototype.checkForRightmostCoordinate = function checkForRightmostCoordinate (de) {
101257             var this$1 = this;
101258
101259           var coord = de.getEdge().getCoordinates();
101260           for (var i = 0; i < coord.length - 1; i++) {
101261             if (this$1._minCoord === null || coord[i].x > this$1._minCoord.x) {
101262               this$1._minDe = de;
101263               this$1._minIndex = i;
101264               this$1._minCoord = coord[i];
101265             }
101266           }
101267         };
101268         RightmostEdgeFinder.prototype.findRightmostEdgeAtNode = function findRightmostEdgeAtNode () {
101269           var node = this._minDe.getNode();
101270           var star = node.getEdges();
101271           this._minDe = star.getRightmostEdge();
101272           if (!this._minDe.isForward()) {
101273             this._minDe = this._minDe.getSym();
101274             this._minIndex = this._minDe.getEdge().getCoordinates().length - 1;
101275           }
101276         };
101277         RightmostEdgeFinder.prototype.findEdge = function findEdge (dirEdgeList) {
101278             var this$1 = this;
101279
101280           for (var i = dirEdgeList.iterator(); i.hasNext();) {
101281             var de = i.next();
101282             if (!de.isForward()) { continue }
101283             this$1.checkForRightmostCoordinate(de);
101284           }
101285           Assert.isTrue(this._minIndex !== 0 || this._minCoord.equals(this._minDe.getCoordinate()), 'inconsistency in rightmost processing');
101286           if (this._minIndex === 0) {
101287             this.findRightmostEdgeAtNode();
101288           } else {
101289             this.findRightmostEdgeAtVertex();
101290           }
101291           this._orientedDe = this._minDe;
101292           var rightmostSide = this.getRightmostSide(this._minDe, this._minIndex);
101293           if (rightmostSide === Position.LEFT) {
101294             this._orientedDe = this._minDe.getSym();
101295           }
101296         };
101297         RightmostEdgeFinder.prototype.interfaces_ = function interfaces_ () {
101298           return []
101299         };
101300         RightmostEdgeFinder.prototype.getClass = function getClass () {
101301           return RightmostEdgeFinder
101302         };
101303
101304         var TopologyException = (function (RuntimeException$$1) {
101305           function TopologyException (msg, pt) {
101306             RuntimeException$$1.call(this, TopologyException.msgWithCoord(msg, pt));
101307             this.pt = pt ? new Coordinate(pt) : null;
101308             this.name = 'TopologyException';
101309           }
101310
101311           if ( RuntimeException$$1 ) { TopologyException.__proto__ = RuntimeException$$1; }
101312           TopologyException.prototype = Object.create( RuntimeException$$1 && RuntimeException$$1.prototype );
101313           TopologyException.prototype.constructor = TopologyException;
101314           TopologyException.prototype.getCoordinate = function getCoordinate () {
101315             return this.pt
101316           };
101317           TopologyException.prototype.interfaces_ = function interfaces_ () {
101318             return []
101319           };
101320           TopologyException.prototype.getClass = function getClass () {
101321             return TopologyException
101322           };
101323           TopologyException.msgWithCoord = function msgWithCoord (msg, pt) {
101324             if (!pt) { return msg + ' [ ' + pt + ' ]' }
101325             return msg
101326           };
101327
101328           return TopologyException;
101329         }(RuntimeException));
101330
101331         var LinkedList = function LinkedList () {
101332           this.array_ = [];
101333         };
101334         LinkedList.prototype.addLast = function addLast (e) {
101335           this.array_.push(e);
101336         };
101337         LinkedList.prototype.removeFirst = function removeFirst () {
101338           return this.array_.shift()
101339         };
101340         LinkedList.prototype.isEmpty = function isEmpty () {
101341           return this.array_.length === 0
101342         };
101343
101344         var BufferSubgraph = function BufferSubgraph () {
101345           this._finder = null;
101346           this._dirEdgeList = new ArrayList();
101347           this._nodes = new ArrayList();
101348           this._rightMostCoord = null;
101349           this._env = null;
101350           this._finder = new RightmostEdgeFinder();
101351         };
101352         BufferSubgraph.prototype.clearVisitedEdges = function clearVisitedEdges () {
101353           for (var it = this._dirEdgeList.iterator(); it.hasNext();) {
101354             var de = it.next();
101355             de.setVisited(false);
101356           }
101357         };
101358         BufferSubgraph.prototype.getRightmostCoordinate = function getRightmostCoordinate () {
101359           return this._rightMostCoord
101360         };
101361         BufferSubgraph.prototype.computeNodeDepth = function computeNodeDepth (n) {
101362             var this$1 = this;
101363
101364           var startEdge = null;
101365           for (var i = n.getEdges().iterator(); i.hasNext();) {
101366             var de = i.next();
101367             if (de.isVisited() || de.getSym().isVisited()) {
101368               startEdge = de;
101369               break
101370             }
101371           }
101372           if (startEdge === null) { throw new TopologyException('unable to find edge to compute depths at ' + n.getCoordinate()) }
101373           n.getEdges().computeDepths(startEdge);
101374           for (var i$1 = n.getEdges().iterator(); i$1.hasNext();) {
101375             var de$1 = i$1.next();
101376             de$1.setVisited(true);
101377             this$1.copySymDepths(de$1);
101378           }
101379         };
101380         BufferSubgraph.prototype.computeDepth = function computeDepth (outsideDepth) {
101381           this.clearVisitedEdges();
101382           var de = this._finder.getEdge();
101383           // const n = de.getNode()
101384           // const label = de.getLabel()
101385           de.setEdgeDepths(Position.RIGHT, outsideDepth);
101386           this.copySymDepths(de);
101387           this.computeDepths(de);
101388         };
101389         BufferSubgraph.prototype.create = function create (node) {
101390           this.addReachable(node);
101391           this._finder.findEdge(this._dirEdgeList);
101392           this._rightMostCoord = this._finder.getCoordinate();
101393         };
101394         BufferSubgraph.prototype.findResultEdges = function findResultEdges () {
101395           for (var it = this._dirEdgeList.iterator(); it.hasNext();) {
101396             var de = it.next();
101397             if (de.getDepth(Position.RIGHT) >= 1 && de.getDepth(Position.LEFT) <= 0 && !de.isInteriorAreaEdge()) {
101398               de.setInResult(true);
101399             }
101400           }
101401         };
101402         BufferSubgraph.prototype.computeDepths = function computeDepths (startEdge) {
101403             var this$1 = this;
101404
101405           var nodesVisited = new HashSet();
101406           var nodeQueue = new LinkedList();
101407           var startNode = startEdge.getNode();
101408           nodeQueue.addLast(startNode);
101409           nodesVisited.add(startNode);
101410           startEdge.setVisited(true);
101411           while (!nodeQueue.isEmpty()) {
101412             var n = nodeQueue.removeFirst();
101413             nodesVisited.add(n);
101414             this$1.computeNodeDepth(n);
101415             for (var i = n.getEdges().iterator(); i.hasNext();) {
101416               var de = i.next();
101417               var sym = de.getSym();
101418               if (sym.isVisited()) { continue }
101419               var adjNode = sym.getNode();
101420               if (!nodesVisited.contains(adjNode)) {
101421                 nodeQueue.addLast(adjNode);
101422                 nodesVisited.add(adjNode);
101423               }
101424             }
101425           }
101426         };
101427         BufferSubgraph.prototype.compareTo = function compareTo (o) {
101428           var graph = o;
101429           if (this._rightMostCoord.x < graph._rightMostCoord.x) {
101430             return -1
101431           }
101432           if (this._rightMostCoord.x > graph._rightMostCoord.x) {
101433             return 1
101434           }
101435           return 0
101436         };
101437         BufferSubgraph.prototype.getEnvelope = function getEnvelope () {
101438           if (this._env === null) {
101439             var edgeEnv = new Envelope();
101440             for (var it = this._dirEdgeList.iterator(); it.hasNext();) {
101441               var dirEdge = it.next();
101442               var pts = dirEdge.getEdge().getCoordinates();
101443               for (var i = 0; i < pts.length - 1; i++) {
101444                 edgeEnv.expandToInclude(pts[i]);
101445               }
101446             }
101447             this._env = edgeEnv;
101448           }
101449           return this._env
101450         };
101451         BufferSubgraph.prototype.addReachable = function addReachable (startNode) {
101452             var this$1 = this;
101453
101454           var nodeStack = new Stack();
101455           nodeStack.add(startNode);
101456           while (!nodeStack.empty()) {
101457             var node = nodeStack.pop();
101458             this$1.add(node, nodeStack);
101459           }
101460         };
101461         BufferSubgraph.prototype.copySymDepths = function copySymDepths (de) {
101462           var sym = de.getSym();
101463           sym.setDepth(Position.LEFT, de.getDepth(Position.RIGHT));
101464           sym.setDepth(Position.RIGHT, de.getDepth(Position.LEFT));
101465         };
101466         BufferSubgraph.prototype.add = function add (node, nodeStack) {
101467             var this$1 = this;
101468
101469           node.setVisited(true);
101470           this._nodes.add(node);
101471           for (var i = node.getEdges().iterator(); i.hasNext();) {
101472             var de = i.next();
101473             this$1._dirEdgeList.add(de);
101474             var sym = de.getSym();
101475             var symNode = sym.getNode();
101476             if (!symNode.isVisited()) { nodeStack.push(symNode); }
101477           }
101478         };
101479         BufferSubgraph.prototype.getNodes = function getNodes () {
101480           return this._nodes
101481         };
101482         BufferSubgraph.prototype.getDirectedEdges = function getDirectedEdges () {
101483           return this._dirEdgeList
101484         };
101485         BufferSubgraph.prototype.interfaces_ = function interfaces_ () {
101486           return [Comparable]
101487         };
101488         BufferSubgraph.prototype.getClass = function getClass () {
101489           return BufferSubgraph
101490         };
101491
101492         var TopologyLocation = function TopologyLocation () {
101493           var this$1 = this;
101494
101495           this.location = null;
101496           if (arguments.length === 1) {
101497             if (arguments[0] instanceof Array) {
101498               var location = arguments[0];
101499               this.init(location.length);
101500             } else if (Number.isInteger(arguments[0])) {
101501               var on = arguments[0];
101502               this.init(1);
101503               this.location[Position.ON] = on;
101504             } else if (arguments[0] instanceof TopologyLocation) {
101505               var gl = arguments[0];
101506               this.init(gl.location.length);
101507               if (gl !== null) {
101508                 for (var i = 0; i < this.location.length; i++) {
101509                   this$1.location[i] = gl.location[i];
101510                 }
101511               }
101512             }
101513           } else if (arguments.length === 3) {
101514             var on$1 = arguments[0];
101515             var left = arguments[1];
101516             var right = arguments[2];
101517             this.init(3);
101518             this.location[Position.ON] = on$1;
101519             this.location[Position.LEFT] = left;
101520             this.location[Position.RIGHT] = right;
101521           }
101522         };
101523         TopologyLocation.prototype.setAllLocations = function setAllLocations (locValue) {
101524             var this$1 = this;
101525
101526           for (var i = 0; i < this.location.length; i++) {
101527             this$1.location[i] = locValue;
101528           }
101529         };
101530         TopologyLocation.prototype.isNull = function isNull () {
101531             var this$1 = this;
101532
101533           for (var i = 0; i < this.location.length; i++) {
101534             if (this$1.location[i] !== Location.NONE) { return false }
101535           }
101536           return true
101537         };
101538         TopologyLocation.prototype.setAllLocationsIfNull = function setAllLocationsIfNull (locValue) {
101539             var this$1 = this;
101540
101541           for (var i = 0; i < this.location.length; i++) {
101542             if (this$1.location[i] === Location.NONE) { this$1.location[i] = locValue; }
101543           }
101544         };
101545         TopologyLocation.prototype.isLine = function isLine () {
101546           return this.location.length === 1
101547         };
101548         TopologyLocation.prototype.merge = function merge (gl) {
101549             var this$1 = this;
101550
101551           if (gl.location.length > this.location.length) {
101552             var newLoc = new Array(3).fill(null);
101553             newLoc[Position.ON] = this.location[Position.ON];
101554             newLoc[Position.LEFT] = Location.NONE;
101555             newLoc[Position.RIGHT] = Location.NONE;
101556             this.location = newLoc;
101557           }
101558           for (var i = 0; i < this.location.length; i++) {
101559             if (this$1.location[i] === Location.NONE && i < gl.location.length) { this$1.location[i] = gl.location[i]; }
101560           }
101561         };
101562         TopologyLocation.prototype.getLocations = function getLocations () {
101563           return this.location
101564         };
101565         TopologyLocation.prototype.flip = function flip () {
101566           if (this.location.length <= 1) { return null }
101567           var temp = this.location[Position.LEFT];
101568           this.location[Position.LEFT] = this.location[Position.RIGHT];
101569           this.location[Position.RIGHT] = temp;
101570         };
101571         TopologyLocation.prototype.toString = function toString () {
101572           var buf = new StringBuffer();
101573           if (this.location.length > 1) { buf.append(Location.toLocationSymbol(this.location[Position.LEFT])); }
101574           buf.append(Location.toLocationSymbol(this.location[Position.ON]));
101575           if (this.location.length > 1) { buf.append(Location.toLocationSymbol(this.location[Position.RIGHT])); }
101576           return buf.toString()
101577         };
101578         TopologyLocation.prototype.setLocations = function setLocations (on, left, right) {
101579           this.location[Position.ON] = on;
101580           this.location[Position.LEFT] = left;
101581           this.location[Position.RIGHT] = right;
101582         };
101583         TopologyLocation.prototype.get = function get (posIndex) {
101584           if (posIndex < this.location.length) { return this.location[posIndex] }
101585           return Location.NONE
101586         };
101587         TopologyLocation.prototype.isArea = function isArea () {
101588           return this.location.length > 1
101589         };
101590         TopologyLocation.prototype.isAnyNull = function isAnyNull () {
101591             var this$1 = this;
101592
101593           for (var i = 0; i < this.location.length; i++) {
101594             if (this$1.location[i] === Location.NONE) { return true }
101595           }
101596           return false
101597         };
101598         TopologyLocation.prototype.setLocation = function setLocation () {
101599           if (arguments.length === 1) {
101600             var locValue = arguments[0];
101601             this.setLocation(Position.ON, locValue);
101602           } else if (arguments.length === 2) {
101603             var locIndex = arguments[0];
101604             var locValue$1 = arguments[1];
101605             this.location[locIndex] = locValue$1;
101606           }
101607         };
101608         TopologyLocation.prototype.init = function init (size) {
101609           this.location = new Array(size).fill(null);
101610           this.setAllLocations(Location.NONE);
101611         };
101612         TopologyLocation.prototype.isEqualOnSide = function isEqualOnSide (le, locIndex) {
101613           return this.location[locIndex] === le.location[locIndex]
101614         };
101615         TopologyLocation.prototype.allPositionsEqual = function allPositionsEqual (loc) {
101616             var this$1 = this;
101617
101618           for (var i = 0; i < this.location.length; i++) {
101619             if (this$1.location[i] !== loc) { return false }
101620           }
101621           return true
101622         };
101623         TopologyLocation.prototype.interfaces_ = function interfaces_ () {
101624           return []
101625         };
101626         TopologyLocation.prototype.getClass = function getClass () {
101627           return TopologyLocation
101628         };
101629
101630         var Label = function Label () {
101631           this.elt = new Array(2).fill(null);
101632           if (arguments.length === 1) {
101633             if (Number.isInteger(arguments[0])) {
101634               var onLoc = arguments[0];
101635               this.elt[0] = new TopologyLocation(onLoc);
101636               this.elt[1] = new TopologyLocation(onLoc);
101637             } else if (arguments[0] instanceof Label) {
101638               var lbl = arguments[0];
101639               this.elt[0] = new TopologyLocation(lbl.elt[0]);
101640               this.elt[1] = new TopologyLocation(lbl.elt[1]);
101641             }
101642           } else if (arguments.length === 2) {
101643             var geomIndex = arguments[0];
101644             var onLoc$1 = arguments[1];
101645             this.elt[0] = new TopologyLocation(Location.NONE);
101646             this.elt[1] = new TopologyLocation(Location.NONE);
101647             this.elt[geomIndex].setLocation(onLoc$1);
101648           } else if (arguments.length === 3) {
101649             var onLoc$2 = arguments[0];
101650             var leftLoc = arguments[1];
101651             var rightLoc = arguments[2];
101652             this.elt[0] = new TopologyLocation(onLoc$2, leftLoc, rightLoc);
101653             this.elt[1] = new TopologyLocation(onLoc$2, leftLoc, rightLoc);
101654           } else if (arguments.length === 4) {
101655             var geomIndex$1 = arguments[0];
101656             var onLoc$3 = arguments[1];
101657             var leftLoc$1 = arguments[2];
101658             var rightLoc$1 = arguments[3];
101659             this.elt[0] = new TopologyLocation(Location.NONE, Location.NONE, Location.NONE);
101660             this.elt[1] = new TopologyLocation(Location.NONE, Location.NONE, Location.NONE);
101661             this.elt[geomIndex$1].setLocations(onLoc$3, leftLoc$1, rightLoc$1);
101662           }
101663         };
101664         Label.prototype.getGeometryCount = function getGeometryCount () {
101665           var count = 0;
101666           if (!this.elt[0].isNull()) { count++; }
101667           if (!this.elt[1].isNull()) { count++; }
101668           return count
101669         };
101670         Label.prototype.setAllLocations = function setAllLocations (geomIndex, location) {
101671           this.elt[geomIndex].setAllLocations(location);
101672         };
101673         Label.prototype.isNull = function isNull (geomIndex) {
101674           return this.elt[geomIndex].isNull()
101675         };
101676         Label.prototype.setAllLocationsIfNull = function setAllLocationsIfNull () {
101677           if (arguments.length === 1) {
101678             var location = arguments[0];
101679             this.setAllLocationsIfNull(0, location);
101680             this.setAllLocationsIfNull(1, location);
101681           } else if (arguments.length === 2) {
101682             var geomIndex = arguments[0];
101683             var location$1 = arguments[1];
101684             this.elt[geomIndex].setAllLocationsIfNull(location$1);
101685           }
101686         };
101687         Label.prototype.isLine = function isLine (geomIndex) {
101688           return this.elt[geomIndex].isLine()
101689         };
101690         Label.prototype.merge = function merge (lbl) {
101691             var this$1 = this;
101692
101693           for (var i = 0; i < 2; i++) {
101694             if (this$1.elt[i] === null && lbl.elt[i] !== null) {
101695               this$1.elt[i] = new TopologyLocation(lbl.elt[i]);
101696             } else {
101697               this$1.elt[i].merge(lbl.elt[i]);
101698             }
101699           }
101700         };
101701         Label.prototype.flip = function flip () {
101702           this.elt[0].flip();
101703           this.elt[1].flip();
101704         };
101705         Label.prototype.getLocation = function getLocation () {
101706           if (arguments.length === 1) {
101707             var geomIndex = arguments[0];
101708             return this.elt[geomIndex].get(Position.ON)
101709           } else if (arguments.length === 2) {
101710             var geomIndex$1 = arguments[0];
101711             var posIndex = arguments[1];
101712             return this.elt[geomIndex$1].get(posIndex)
101713           }
101714         };
101715         Label.prototype.toString = function toString () {
101716           var buf = new StringBuffer();
101717           if (this.elt[0] !== null) {
101718             buf.append('A:');
101719             buf.append(this.elt[0].toString());
101720           }
101721           if (this.elt[1] !== null) {
101722             buf.append(' B:');
101723             buf.append(this.elt[1].toString());
101724           }
101725           return buf.toString()
101726         };
101727         Label.prototype.isArea = function isArea () {
101728           if (arguments.length === 0) {
101729             return this.elt[0].isArea() || this.elt[1].isArea()
101730           } else if (arguments.length === 1) {
101731             var geomIndex = arguments[0];
101732             return this.elt[geomIndex].isArea()
101733           }
101734         };
101735         Label.prototype.isAnyNull = function isAnyNull (geomIndex) {
101736           return this.elt[geomIndex].isAnyNull()
101737         };
101738         Label.prototype.setLocation = function setLocation () {
101739           if (arguments.length === 2) {
101740             var geomIndex = arguments[0];
101741             var location = arguments[1];
101742             this.elt[geomIndex].setLocation(Position.ON, location);
101743           } else if (arguments.length === 3) {
101744             var geomIndex$1 = arguments[0];
101745             var posIndex = arguments[1];
101746             var location$1 = arguments[2];
101747             this.elt[geomIndex$1].setLocation(posIndex, location$1);
101748           }
101749         };
101750         Label.prototype.isEqualOnSide = function isEqualOnSide (lbl, side) {
101751           return this.elt[0].isEqualOnSide(lbl.elt[0], side) && this.elt[1].isEqualOnSide(lbl.elt[1], side)
101752         };
101753         Label.prototype.allPositionsEqual = function allPositionsEqual (geomIndex, loc) {
101754           return this.elt[geomIndex].allPositionsEqual(loc)
101755         };
101756         Label.prototype.toLine = function toLine (geomIndex) {
101757           if (this.elt[geomIndex].isArea()) { this.elt[geomIndex] = new TopologyLocation(this.elt[geomIndex].location[0]); }
101758         };
101759         Label.prototype.interfaces_ = function interfaces_ () {
101760           return []
101761         };
101762         Label.prototype.getClass = function getClass () {
101763           return Label
101764         };
101765         Label.toLineLabel = function toLineLabel (label) {
101766           var lineLabel = new Label(Location.NONE);
101767           for (var i = 0; i < 2; i++) {
101768             lineLabel.setLocation(i, label.getLocation(i));
101769           }
101770           return lineLabel
101771         };
101772
101773         var EdgeRing = function EdgeRing () {
101774           this._startDe = null;
101775           this._maxNodeDegree = -1;
101776           this._edges = new ArrayList();
101777           this._pts = new ArrayList();
101778           this._label = new Label(Location.NONE);
101779           this._ring = null;
101780           this._isHole = null;
101781           this._shell = null;
101782           this._holes = new ArrayList();
101783           this._geometryFactory = null;
101784           var start = arguments[0];
101785           var geometryFactory = arguments[1];
101786           this._geometryFactory = geometryFactory;
101787           this.computePoints(start);
101788           this.computeRing();
101789         };
101790         EdgeRing.prototype.computeRing = function computeRing () {
101791             var this$1 = this;
101792
101793           if (this._ring !== null) { return null }
101794           var coord = new Array(this._pts.size()).fill(null);
101795           for (var i = 0; i < this._pts.size(); i++) {
101796             coord[i] = this$1._pts.get(i);
101797           }
101798           this._ring = this._geometryFactory.createLinearRing(coord);
101799           this._isHole = CGAlgorithms.isCCW(this._ring.getCoordinates());
101800         };
101801         EdgeRing.prototype.isIsolated = function isIsolated () {
101802           return this._label.getGeometryCount() === 1
101803         };
101804         EdgeRing.prototype.computePoints = function computePoints (start) {
101805             var this$1 = this;
101806
101807           this._startDe = start;
101808           var de = start;
101809           var isFirstEdge = true;
101810           do {
101811             if (de === null) { throw new TopologyException('Found null DirectedEdge') }
101812             if (de.getEdgeRing() === this$1) { throw new TopologyException('Directed Edge visited twice during ring-building at ' + de.getCoordinate()) }
101813             this$1._edges.add(de);
101814             var label = de.getLabel();
101815             Assert.isTrue(label.isArea());
101816             this$1.mergeLabel(label);
101817             this$1.addPoints(de.getEdge(), de.isForward(), isFirstEdge);
101818             isFirstEdge = false;
101819             this$1.setEdgeRing(de, this$1);
101820             de = this$1.getNext(de);
101821           } while (de !== this._startDe)
101822         };
101823         EdgeRing.prototype.getLinearRing = function getLinearRing () {
101824           return this._ring
101825         };
101826         EdgeRing.prototype.getCoordinate = function getCoordinate (i) {
101827           return this._pts.get(i)
101828         };
101829         EdgeRing.prototype.computeMaxNodeDegree = function computeMaxNodeDegree () {
101830             var this$1 = this;
101831
101832           this._maxNodeDegree = 0;
101833           var de = this._startDe;
101834           do {
101835             var node = de.getNode();
101836             var degree = node.getEdges().getOutgoingDegree(this$1);
101837             if (degree > this$1._maxNodeDegree) { this$1._maxNodeDegree = degree; }
101838             de = this$1.getNext(de);
101839           } while (de !== this._startDe)
101840           this._maxNodeDegree *= 2;
101841         };
101842         EdgeRing.prototype.addPoints = function addPoints (edge, isForward, isFirstEdge) {
101843             var this$1 = this;
101844
101845           var edgePts = edge.getCoordinates();
101846           if (isForward) {
101847             var startIndex = 1;
101848             if (isFirstEdge) { startIndex = 0; }
101849             for (var i = startIndex; i < edgePts.length; i++) {
101850               this$1._pts.add(edgePts[i]);
101851             }
101852           } else {
101853             var startIndex$1 = edgePts.length - 2;
101854             if (isFirstEdge) { startIndex$1 = edgePts.length - 1; }
101855             for (var i$1 = startIndex$1; i$1 >= 0; i$1--) {
101856               this$1._pts.add(edgePts[i$1]);
101857             }
101858           }
101859         };
101860         EdgeRing.prototype.isHole = function isHole () {
101861           return this._isHole
101862         };
101863         EdgeRing.prototype.setInResult = function setInResult () {
101864           var de = this._startDe;
101865           do {
101866             de.getEdge().setInResult(true);
101867             de = de.getNext();
101868           } while (de !== this._startDe)
101869         };
101870         EdgeRing.prototype.containsPoint = function containsPoint (p) {
101871           var shell = this.getLinearRing();
101872           var env = shell.getEnvelopeInternal();
101873           if (!env.contains(p)) { return false }
101874           if (!CGAlgorithms.isPointInRing(p, shell.getCoordinates())) { return false }
101875           for (var i = this._holes.iterator(); i.hasNext();) {
101876             var hole = i.next();
101877             if (hole.containsPoint(p)) { return false }
101878           }
101879           return true
101880         };
101881         EdgeRing.prototype.addHole = function addHole (ring) {
101882           this._holes.add(ring);
101883         };
101884         EdgeRing.prototype.isShell = function isShell () {
101885           return this._shell === null
101886         };
101887         EdgeRing.prototype.getLabel = function getLabel () {
101888           return this._label
101889         };
101890         EdgeRing.prototype.getEdges = function getEdges () {
101891           return this._edges
101892         };
101893         EdgeRing.prototype.getMaxNodeDegree = function getMaxNodeDegree () {
101894           if (this._maxNodeDegree < 0) { this.computeMaxNodeDegree(); }
101895           return this._maxNodeDegree
101896         };
101897         EdgeRing.prototype.getShell = function getShell () {
101898           return this._shell
101899         };
101900         EdgeRing.prototype.mergeLabel = function mergeLabel () {
101901           if (arguments.length === 1) {
101902             var deLabel = arguments[0];
101903             this.mergeLabel(deLabel, 0);
101904             this.mergeLabel(deLabel, 1);
101905           } else if (arguments.length === 2) {
101906             var deLabel$1 = arguments[0];
101907             var geomIndex = arguments[1];
101908             var loc = deLabel$1.getLocation(geomIndex, Position.RIGHT);
101909             if (loc === Location.NONE) { return null }
101910             if (this._label.getLocation(geomIndex) === Location.NONE) {
101911               this._label.setLocation(geomIndex, loc);
101912               return null
101913             }
101914           }
101915         };
101916         EdgeRing.prototype.setShell = function setShell (shell) {
101917           this._shell = shell;
101918           if (shell !== null) { shell.addHole(this); }
101919         };
101920         EdgeRing.prototype.toPolygon = function toPolygon (geometryFactory) {
101921             var this$1 = this;
101922
101923           var holeLR = new Array(this._holes.size()).fill(null);
101924           for (var i = 0; i < this._holes.size(); i++) {
101925             holeLR[i] = this$1._holes.get(i).getLinearRing();
101926           }
101927           var poly = geometryFactory.createPolygon(this.getLinearRing(), holeLR);
101928           return poly
101929         };
101930         EdgeRing.prototype.interfaces_ = function interfaces_ () {
101931           return []
101932         };
101933         EdgeRing.prototype.getClass = function getClass () {
101934           return EdgeRing
101935         };
101936
101937         var MinimalEdgeRing = (function (EdgeRing$$1) {
101938           function MinimalEdgeRing () {
101939             var start = arguments[0];
101940             var geometryFactory = arguments[1];
101941             EdgeRing$$1.call(this, start, geometryFactory);
101942           }
101943
101944           if ( EdgeRing$$1 ) { MinimalEdgeRing.__proto__ = EdgeRing$$1; }
101945           MinimalEdgeRing.prototype = Object.create( EdgeRing$$1 && EdgeRing$$1.prototype );
101946           MinimalEdgeRing.prototype.constructor = MinimalEdgeRing;
101947           MinimalEdgeRing.prototype.setEdgeRing = function setEdgeRing (de, er) {
101948             de.setMinEdgeRing(er);
101949           };
101950           MinimalEdgeRing.prototype.getNext = function getNext (de) {
101951             return de.getNextMin()
101952           };
101953           MinimalEdgeRing.prototype.interfaces_ = function interfaces_ () {
101954             return []
101955           };
101956           MinimalEdgeRing.prototype.getClass = function getClass () {
101957             return MinimalEdgeRing
101958           };
101959
101960           return MinimalEdgeRing;
101961         }(EdgeRing));
101962
101963         var MaximalEdgeRing = (function (EdgeRing$$1) {
101964           function MaximalEdgeRing () {
101965             var start = arguments[0];
101966             var geometryFactory = arguments[1];
101967             EdgeRing$$1.call(this, start, geometryFactory);
101968           }
101969
101970           if ( EdgeRing$$1 ) { MaximalEdgeRing.__proto__ = EdgeRing$$1; }
101971           MaximalEdgeRing.prototype = Object.create( EdgeRing$$1 && EdgeRing$$1.prototype );
101972           MaximalEdgeRing.prototype.constructor = MaximalEdgeRing;
101973           MaximalEdgeRing.prototype.buildMinimalRings = function buildMinimalRings () {
101974             var this$1 = this;
101975
101976             var minEdgeRings = new ArrayList();
101977             var de = this._startDe;
101978             do {
101979               if (de.getMinEdgeRing() === null) {
101980                 var minEr = new MinimalEdgeRing(de, this$1._geometryFactory);
101981                 minEdgeRings.add(minEr);
101982               }
101983               de = de.getNext();
101984             } while (de !== this._startDe)
101985             return minEdgeRings
101986           };
101987           MaximalEdgeRing.prototype.setEdgeRing = function setEdgeRing (de, er) {
101988             de.setEdgeRing(er);
101989           };
101990           MaximalEdgeRing.prototype.linkDirectedEdgesForMinimalEdgeRings = function linkDirectedEdgesForMinimalEdgeRings () {
101991             var this$1 = this;
101992
101993             var de = this._startDe;
101994             do {
101995               var node = de.getNode();
101996               node.getEdges().linkMinimalDirectedEdges(this$1);
101997               de = de.getNext();
101998             } while (de !== this._startDe)
101999           };
102000           MaximalEdgeRing.prototype.getNext = function getNext (de) {
102001             return de.getNext()
102002           };
102003           MaximalEdgeRing.prototype.interfaces_ = function interfaces_ () {
102004             return []
102005           };
102006           MaximalEdgeRing.prototype.getClass = function getClass () {
102007             return MaximalEdgeRing
102008           };
102009
102010           return MaximalEdgeRing;
102011         }(EdgeRing));
102012
102013         var GraphComponent = function GraphComponent () {
102014           this._label = null;
102015           this._isInResult = false;
102016           this._isCovered = false;
102017           this._isCoveredSet = false;
102018           this._isVisited = false;
102019           if (arguments.length === 0) ; else if (arguments.length === 1) {
102020             var label = arguments[0];
102021             this._label = label;
102022           }
102023         };
102024         GraphComponent.prototype.setVisited = function setVisited (isVisited) {
102025           this._isVisited = isVisited;
102026         };
102027         GraphComponent.prototype.setInResult = function setInResult (isInResult) {
102028           this._isInResult = isInResult;
102029         };
102030         GraphComponent.prototype.isCovered = function isCovered () {
102031           return this._isCovered
102032         };
102033         GraphComponent.prototype.isCoveredSet = function isCoveredSet () {
102034           return this._isCoveredSet
102035         };
102036         GraphComponent.prototype.setLabel = function setLabel (label) {
102037           this._label = label;
102038         };
102039         GraphComponent.prototype.getLabel = function getLabel () {
102040           return this._label
102041         };
102042         GraphComponent.prototype.setCovered = function setCovered (isCovered) {
102043           this._isCovered = isCovered;
102044           this._isCoveredSet = true;
102045         };
102046         GraphComponent.prototype.updateIM = function updateIM (im) {
102047           Assert.isTrue(this._label.getGeometryCount() >= 2, 'found partial label');
102048           this.computeIM(im);
102049         };
102050         GraphComponent.prototype.isInResult = function isInResult () {
102051           return this._isInResult
102052         };
102053         GraphComponent.prototype.isVisited = function isVisited () {
102054           return this._isVisited
102055         };
102056         GraphComponent.prototype.interfaces_ = function interfaces_ () {
102057           return []
102058         };
102059         GraphComponent.prototype.getClass = function getClass () {
102060           return GraphComponent
102061         };
102062
102063         var Node$1 = (function (GraphComponent$$1) {
102064           function Node () {
102065             GraphComponent$$1.call(this);
102066             this._coord = null;
102067             this._edges = null;
102068             var coord = arguments[0];
102069             var edges = arguments[1];
102070             this._coord = coord;
102071             this._edges = edges;
102072             this._label = new Label(0, Location.NONE);
102073           }
102074
102075           if ( GraphComponent$$1 ) { Node.__proto__ = GraphComponent$$1; }
102076           Node.prototype = Object.create( GraphComponent$$1 && GraphComponent$$1.prototype );
102077           Node.prototype.constructor = Node;
102078           Node.prototype.isIncidentEdgeInResult = function isIncidentEdgeInResult () {
102079             for (var it = this.getEdges().getEdges().iterator(); it.hasNext();) {
102080               var de = it.next();
102081               if (de.getEdge().isInResult()) { return true }
102082             }
102083             return false
102084           };
102085           Node.prototype.isIsolated = function isIsolated () {
102086             return this._label.getGeometryCount() === 1
102087           };
102088           Node.prototype.getCoordinate = function getCoordinate () {
102089             return this._coord
102090           };
102091           Node.prototype.print = function print (out) {
102092             out.println('node ' + this._coord + ' lbl: ' + this._label);
102093           };
102094           Node.prototype.computeIM = function computeIM (im) {};
102095           Node.prototype.computeMergedLocation = function computeMergedLocation (label2, eltIndex) {
102096             var loc = Location.NONE;
102097             loc = this._label.getLocation(eltIndex);
102098             if (!label2.isNull(eltIndex)) {
102099               var nLoc = label2.getLocation(eltIndex);
102100               if (loc !== Location.BOUNDARY) { loc = nLoc; }
102101             }
102102             return loc
102103           };
102104           Node.prototype.setLabel = function setLabel () {
102105             if (arguments.length === 2) {
102106               var argIndex = arguments[0];
102107               var onLocation = arguments[1];
102108               if (this._label === null) {
102109                 this._label = new Label(argIndex, onLocation);
102110               } else { this._label.setLocation(argIndex, onLocation); }
102111             } else { return GraphComponent$$1.prototype.setLabel.apply(this, arguments) }
102112           };
102113           Node.prototype.getEdges = function getEdges () {
102114             return this._edges
102115           };
102116           Node.prototype.mergeLabel = function mergeLabel () {
102117             var this$1 = this;
102118
102119             if (arguments[0] instanceof Node) {
102120               var n = arguments[0];
102121               this.mergeLabel(n._label);
102122             } else if (arguments[0] instanceof Label) {
102123               var label2 = arguments[0];
102124               for (var i = 0; i < 2; i++) {
102125                 var loc = this$1.computeMergedLocation(label2, i);
102126                 var thisLoc = this$1._label.getLocation(i);
102127                 if (thisLoc === Location.NONE) { this$1._label.setLocation(i, loc); }
102128               }
102129             }
102130           };
102131           Node.prototype.add = function add (e) {
102132             this._edges.insert(e);
102133             e.setNode(this);
102134           };
102135           Node.prototype.setLabelBoundary = function setLabelBoundary (argIndex) {
102136             if (this._label === null) { return null }
102137             var loc = Location.NONE;
102138             if (this._label !== null) { loc = this._label.getLocation(argIndex); }
102139             var newLoc = null;
102140             switch (loc) {
102141               case Location.BOUNDARY:
102142                 newLoc = Location.INTERIOR;
102143                 break
102144               case Location.INTERIOR:
102145                 newLoc = Location.BOUNDARY;
102146                 break
102147               default:
102148                 newLoc = Location.BOUNDARY;
102149                 break
102150             }
102151             this._label.setLocation(argIndex, newLoc);
102152           };
102153           Node.prototype.interfaces_ = function interfaces_ () {
102154             return []
102155           };
102156           Node.prototype.getClass = function getClass () {
102157             return Node
102158           };
102159
102160           return Node;
102161         }(GraphComponent));
102162
102163         var NodeMap = function NodeMap () {
102164           this.nodeMap = new TreeMap();
102165           this.nodeFact = null;
102166           var nodeFact = arguments[0];
102167           this.nodeFact = nodeFact;
102168         };
102169         NodeMap.prototype.find = function find (coord) {
102170           return this.nodeMap.get(coord)
102171         };
102172         NodeMap.prototype.addNode = function addNode () {
102173           if (arguments[0] instanceof Coordinate) {
102174             var coord = arguments[0];
102175             var node = this.nodeMap.get(coord);
102176             if (node === null) {
102177               node = this.nodeFact.createNode(coord);
102178               this.nodeMap.put(coord, node);
102179             }
102180             return node
102181           } else if (arguments[0] instanceof Node$1) {
102182             var n = arguments[0];
102183             var node$1 = this.nodeMap.get(n.getCoordinate());
102184             if (node$1 === null) {
102185               this.nodeMap.put(n.getCoordinate(), n);
102186               return n
102187             }
102188             node$1.mergeLabel(n);
102189             return node$1
102190           }
102191         };
102192         NodeMap.prototype.print = function print (out) {
102193           for (var it = this.iterator(); it.hasNext();) {
102194             var n = it.next();
102195             n.print(out);
102196           }
102197         };
102198         NodeMap.prototype.iterator = function iterator () {
102199           return this.nodeMap.values().iterator()
102200         };
102201         NodeMap.prototype.values = function values () {
102202           return this.nodeMap.values()
102203         };
102204         NodeMap.prototype.getBoundaryNodes = function getBoundaryNodes (geomIndex) {
102205           var bdyNodes = new ArrayList();
102206           for (var i = this.iterator(); i.hasNext();) {
102207             var node = i.next();
102208             if (node.getLabel().getLocation(geomIndex) === Location.BOUNDARY) { bdyNodes.add(node); }
102209           }
102210           return bdyNodes
102211         };
102212         NodeMap.prototype.add = function add (e) {
102213           var p = e.getCoordinate();
102214           var n = this.addNode(p);
102215           n.add(e);
102216         };
102217         NodeMap.prototype.interfaces_ = function interfaces_ () {
102218           return []
102219         };
102220         NodeMap.prototype.getClass = function getClass () {
102221           return NodeMap
102222         };
102223
102224         var Quadrant = function Quadrant () {};
102225
102226         var staticAccessors$21 = { NE: { configurable: true },NW: { configurable: true },SW: { configurable: true },SE: { configurable: true } };
102227
102228         Quadrant.prototype.interfaces_ = function interfaces_ () {
102229           return []
102230         };
102231         Quadrant.prototype.getClass = function getClass () {
102232           return Quadrant
102233         };
102234         Quadrant.isNorthern = function isNorthern (quad) {
102235           return quad === Quadrant.NE || quad === Quadrant.NW
102236         };
102237         Quadrant.isOpposite = function isOpposite (quad1, quad2) {
102238           if (quad1 === quad2) { return false }
102239           var diff = (quad1 - quad2 + 4) % 4;
102240           if (diff === 2) { return true }
102241           return false
102242         };
102243         Quadrant.commonHalfPlane = function commonHalfPlane (quad1, quad2) {
102244           if (quad1 === quad2) { return quad1 }
102245           var diff = (quad1 - quad2 + 4) % 4;
102246           if (diff === 2) { return -1 }
102247           var min = quad1 < quad2 ? quad1 : quad2;
102248           var max = quad1 > quad2 ? quad1 : quad2;
102249           if (min === 0 && max === 3) { return 3 }
102250           return min
102251         };
102252         Quadrant.isInHalfPlane = function isInHalfPlane (quad, halfPlane) {
102253           if (halfPlane === Quadrant.SE) {
102254             return quad === Quadrant.SE || quad === Quadrant.SW
102255           }
102256           return quad === halfPlane || quad === halfPlane + 1
102257         };
102258         Quadrant.quadrant = function quadrant () {
102259           if (typeof arguments[0] === 'number' && typeof arguments[1] === 'number') {
102260             var dx = arguments[0];
102261             var dy = arguments[1];
102262             if (dx === 0.0 && dy === 0.0) { throw new IllegalArgumentException('Cannot compute the quadrant for point ( ' + dx + ', ' + dy + ' )') }
102263             if (dx >= 0.0) {
102264               if (dy >= 0.0) { return Quadrant.NE; } else { return Quadrant.SE }
102265             } else {
102266               if (dy >= 0.0) { return Quadrant.NW; } else { return Quadrant.SW }
102267             }
102268           } else if (arguments[0] instanceof Coordinate && arguments[1] instanceof Coordinate) {
102269             var p0 = arguments[0];
102270             var p1 = arguments[1];
102271             if (p1.x === p0.x && p1.y === p0.y) { throw new IllegalArgumentException('Cannot compute the quadrant for two identical points ' + p0) }
102272             if (p1.x >= p0.x) {
102273               if (p1.y >= p0.y) { return Quadrant.NE; } else { return Quadrant.SE }
102274             } else {
102275               if (p1.y >= p0.y) { return Quadrant.NW; } else { return Quadrant.SW }
102276             }
102277           }
102278         };
102279         staticAccessors$21.NE.get = function () { return 0 };
102280         staticAccessors$21.NW.get = function () { return 1 };
102281         staticAccessors$21.SW.get = function () { return 2 };
102282         staticAccessors$21.SE.get = function () { return 3 };
102283
102284         Object.defineProperties( Quadrant, staticAccessors$21 );
102285
102286         var EdgeEnd = function EdgeEnd () {
102287           this._edge = null;
102288           this._label = null;
102289           this._node = null;
102290           this._p0 = null;
102291           this._p1 = null;
102292           this._dx = null;
102293           this._dy = null;
102294           this._quadrant = null;
102295           if (arguments.length === 1) {
102296             var edge = arguments[0];
102297             this._edge = edge;
102298           } else if (arguments.length === 3) {
102299             var edge$1 = arguments[0];
102300             var p0 = arguments[1];
102301             var p1 = arguments[2];
102302             var label = null;
102303             this._edge = edge$1;
102304             this.init(p0, p1);
102305             this._label = label;
102306           } else if (arguments.length === 4) {
102307             var edge$2 = arguments[0];
102308             var p0$1 = arguments[1];
102309             var p1$1 = arguments[2];
102310             var label$1 = arguments[3];
102311             this._edge = edge$2;
102312             this.init(p0$1, p1$1);
102313             this._label = label$1;
102314           }
102315         };
102316         EdgeEnd.prototype.compareDirection = function compareDirection (e) {
102317           if (this._dx === e._dx && this._dy === e._dy) { return 0 }
102318           if (this._quadrant > e._quadrant) { return 1 }
102319           if (this._quadrant < e._quadrant) { return -1 }
102320           return CGAlgorithms.computeOrientation(e._p0, e._p1, this._p1)
102321         };
102322         EdgeEnd.prototype.getDy = function getDy () {
102323           return this._dy
102324         };
102325         EdgeEnd.prototype.getCoordinate = function getCoordinate () {
102326           return this._p0
102327         };
102328         EdgeEnd.prototype.setNode = function setNode (node) {
102329           this._node = node;
102330         };
102331         EdgeEnd.prototype.print = function print (out) {
102332           var angle = Math.atan2(this._dy, this._dx);
102333           var className = this.getClass().getName();
102334           var lastDotPos = className.lastIndexOf('.');
102335           var name = className.substring(lastDotPos + 1);
102336           out.print('  ' + name + ': ' + this._p0 + ' - ' + this._p1 + ' ' + this._quadrant + ':' + angle + '   ' + this._label);
102337         };
102338         EdgeEnd.prototype.compareTo = function compareTo (obj) {
102339           var e = obj;
102340           return this.compareDirection(e)
102341         };
102342         EdgeEnd.prototype.getDirectedCoordinate = function getDirectedCoordinate () {
102343           return this._p1
102344         };
102345         EdgeEnd.prototype.getDx = function getDx () {
102346           return this._dx
102347         };
102348         EdgeEnd.prototype.getLabel = function getLabel () {
102349           return this._label
102350         };
102351         EdgeEnd.prototype.getEdge = function getEdge () {
102352           return this._edge
102353         };
102354         EdgeEnd.prototype.getQuadrant = function getQuadrant () {
102355           return this._quadrant
102356         };
102357         EdgeEnd.prototype.getNode = function getNode () {
102358           return this._node
102359         };
102360         EdgeEnd.prototype.toString = function toString () {
102361           var angle = Math.atan2(this._dy, this._dx);
102362           var className = this.getClass().getName();
102363           var lastDotPos = className.lastIndexOf('.');
102364           var name = className.substring(lastDotPos + 1);
102365           return '  ' + name + ': ' + this._p0 + ' - ' + this._p1 + ' ' + this._quadrant + ':' + angle + '   ' + this._label
102366         };
102367         EdgeEnd.prototype.computeLabel = function computeLabel (boundaryNodeRule) {};
102368         EdgeEnd.prototype.init = function init (p0, p1) {
102369           this._p0 = p0;
102370           this._p1 = p1;
102371           this._dx = p1.x - p0.x;
102372           this._dy = p1.y - p0.y;
102373           this._quadrant = Quadrant.quadrant(this._dx, this._dy);
102374           Assert.isTrue(!(this._dx === 0 && this._dy === 0), 'EdgeEnd with identical endpoints found');
102375         };
102376         EdgeEnd.prototype.interfaces_ = function interfaces_ () {
102377           return [Comparable]
102378         };
102379         EdgeEnd.prototype.getClass = function getClass () {
102380           return EdgeEnd
102381         };
102382
102383         var DirectedEdge = (function (EdgeEnd$$1) {
102384           function DirectedEdge () {
102385             var edge = arguments[0];
102386             var isForward = arguments[1];
102387             EdgeEnd$$1.call(this, edge);
102388             this._isForward = null;
102389             this._isInResult = false;
102390             this._isVisited = false;
102391             this._sym = null;
102392             this._next = null;
102393             this._nextMin = null;
102394             this._edgeRing = null;
102395             this._minEdgeRing = null;
102396             this._depth = [0, -999, -999];
102397             this._isForward = isForward;
102398             if (isForward) {
102399               this.init(edge.getCoordinate(0), edge.getCoordinate(1));
102400             } else {
102401               var n = edge.getNumPoints() - 1;
102402               this.init(edge.getCoordinate(n), edge.getCoordinate(n - 1));
102403             }
102404             this.computeDirectedLabel();
102405           }
102406
102407           if ( EdgeEnd$$1 ) { DirectedEdge.__proto__ = EdgeEnd$$1; }
102408           DirectedEdge.prototype = Object.create( EdgeEnd$$1 && EdgeEnd$$1.prototype );
102409           DirectedEdge.prototype.constructor = DirectedEdge;
102410           DirectedEdge.prototype.getNextMin = function getNextMin () {
102411             return this._nextMin
102412           };
102413           DirectedEdge.prototype.getDepth = function getDepth (position) {
102414             return this._depth[position]
102415           };
102416           DirectedEdge.prototype.setVisited = function setVisited (isVisited) {
102417             this._isVisited = isVisited;
102418           };
102419           DirectedEdge.prototype.computeDirectedLabel = function computeDirectedLabel () {
102420             this._label = new Label(this._edge.getLabel());
102421             if (!this._isForward) { this._label.flip(); }
102422           };
102423           DirectedEdge.prototype.getNext = function getNext () {
102424             return this._next
102425           };
102426           DirectedEdge.prototype.setDepth = function setDepth (position, depthVal) {
102427             if (this._depth[position] !== -999) {
102428               if (this._depth[position] !== depthVal) { throw new TopologyException('assigned depths do not match', this.getCoordinate()) }
102429             }
102430             this._depth[position] = depthVal;
102431           };
102432           DirectedEdge.prototype.isInteriorAreaEdge = function isInteriorAreaEdge () {
102433             var this$1 = this;
102434
102435             var isInteriorAreaEdge = true;
102436             for (var i = 0; i < 2; i++) {
102437               if (!(this$1._label.isArea(i) && this$1._label.getLocation(i, Position.LEFT) === Location.INTERIOR && this$1._label.getLocation(i, Position.RIGHT) === Location.INTERIOR)) {
102438                 isInteriorAreaEdge = false;
102439               }
102440             }
102441             return isInteriorAreaEdge
102442           };
102443           DirectedEdge.prototype.setNextMin = function setNextMin (nextMin) {
102444             this._nextMin = nextMin;
102445           };
102446           DirectedEdge.prototype.print = function print (out) {
102447             EdgeEnd$$1.prototype.print.call(this, out);
102448             out.print(' ' + this._depth[Position.LEFT] + '/' + this._depth[Position.RIGHT]);
102449             out.print(' (' + this.getDepthDelta() + ')');
102450             if (this._isInResult) { out.print(' inResult'); }
102451           };
102452           DirectedEdge.prototype.setMinEdgeRing = function setMinEdgeRing (minEdgeRing) {
102453             this._minEdgeRing = minEdgeRing;
102454           };
102455           DirectedEdge.prototype.isLineEdge = function isLineEdge () {
102456             var isLine = this._label.isLine(0) || this._label.isLine(1);
102457             var isExteriorIfArea0 = !this._label.isArea(0) || this._label.allPositionsEqual(0, Location.EXTERIOR);
102458             var isExteriorIfArea1 = !this._label.isArea(1) || this._label.allPositionsEqual(1, Location.EXTERIOR);
102459             return isLine && isExteriorIfArea0 && isExteriorIfArea1
102460           };
102461           DirectedEdge.prototype.setEdgeRing = function setEdgeRing (edgeRing) {
102462             this._edgeRing = edgeRing;
102463           };
102464           DirectedEdge.prototype.getMinEdgeRing = function getMinEdgeRing () {
102465             return this._minEdgeRing
102466           };
102467           DirectedEdge.prototype.getDepthDelta = function getDepthDelta () {
102468             var depthDelta = this._edge.getDepthDelta();
102469             if (!this._isForward) { depthDelta = -depthDelta; }
102470             return depthDelta
102471           };
102472           DirectedEdge.prototype.setInResult = function setInResult (isInResult) {
102473             this._isInResult = isInResult;
102474           };
102475           DirectedEdge.prototype.getSym = function getSym () {
102476             return this._sym
102477           };
102478           DirectedEdge.prototype.isForward = function isForward () {
102479             return this._isForward
102480           };
102481           DirectedEdge.prototype.getEdge = function getEdge () {
102482             return this._edge
102483           };
102484           DirectedEdge.prototype.printEdge = function printEdge (out) {
102485             this.print(out);
102486             out.print(' ');
102487             if (this._isForward) { this._edge.print(out); } else { this._edge.printReverse(out); }
102488           };
102489           DirectedEdge.prototype.setSym = function setSym (de) {
102490             this._sym = de;
102491           };
102492           DirectedEdge.prototype.setVisitedEdge = function setVisitedEdge (isVisited) {
102493             this.setVisited(isVisited);
102494             this._sym.setVisited(isVisited);
102495           };
102496           DirectedEdge.prototype.setEdgeDepths = function setEdgeDepths (position, depth) {
102497             var depthDelta = this.getEdge().getDepthDelta();
102498             if (!this._isForward) { depthDelta = -depthDelta; }
102499             var directionFactor = 1;
102500             if (position === Position.LEFT) { directionFactor = -1; }
102501             var oppositePos = Position.opposite(position);
102502             var delta = depthDelta * directionFactor;
102503             var oppositeDepth = depth + delta;
102504             this.setDepth(position, depth);
102505             this.setDepth(oppositePos, oppositeDepth);
102506           };
102507           DirectedEdge.prototype.getEdgeRing = function getEdgeRing () {
102508             return this._edgeRing
102509           };
102510           DirectedEdge.prototype.isInResult = function isInResult () {
102511             return this._isInResult
102512           };
102513           DirectedEdge.prototype.setNext = function setNext (next) {
102514             this._next = next;
102515           };
102516           DirectedEdge.prototype.isVisited = function isVisited () {
102517             return this._isVisited
102518           };
102519           DirectedEdge.prototype.interfaces_ = function interfaces_ () {
102520             return []
102521           };
102522           DirectedEdge.prototype.getClass = function getClass () {
102523             return DirectedEdge
102524           };
102525           DirectedEdge.depthFactor = function depthFactor (currLocation, nextLocation) {
102526             if (currLocation === Location.EXTERIOR && nextLocation === Location.INTERIOR) { return 1; } else if (currLocation === Location.INTERIOR && nextLocation === Location.EXTERIOR) { return -1 }
102527             return 0
102528           };
102529
102530           return DirectedEdge;
102531         }(EdgeEnd));
102532
102533         var NodeFactory = function NodeFactory () {};
102534
102535         NodeFactory.prototype.createNode = function createNode (coord) {
102536           return new Node$1(coord, null)
102537         };
102538         NodeFactory.prototype.interfaces_ = function interfaces_ () {
102539           return []
102540         };
102541         NodeFactory.prototype.getClass = function getClass () {
102542           return NodeFactory
102543         };
102544
102545         var PlanarGraph = function PlanarGraph () {
102546           this._edges = new ArrayList();
102547           this._nodes = null;
102548           this._edgeEndList = new ArrayList();
102549           if (arguments.length === 0) {
102550             this._nodes = new NodeMap(new NodeFactory());
102551           } else if (arguments.length === 1) {
102552             var nodeFact = arguments[0];
102553             this._nodes = new NodeMap(nodeFact);
102554           }
102555         };
102556         PlanarGraph.prototype.printEdges = function printEdges (out) {
102557             var this$1 = this;
102558
102559           out.println('Edges:');
102560           for (var i = 0; i < this._edges.size(); i++) {
102561             out.println('edge ' + i + ':');
102562             var e = this$1._edges.get(i);
102563             e.print(out);
102564             e.eiList.print(out);
102565           }
102566         };
102567         PlanarGraph.prototype.find = function find (coord) {
102568           return this._nodes.find(coord)
102569         };
102570         PlanarGraph.prototype.addNode = function addNode () {
102571           if (arguments[0] instanceof Node$1) {
102572             var node = arguments[0];
102573             return this._nodes.addNode(node)
102574           } else if (arguments[0] instanceof Coordinate) {
102575             var coord = arguments[0];
102576             return this._nodes.addNode(coord)
102577           }
102578         };
102579         PlanarGraph.prototype.getNodeIterator = function getNodeIterator () {
102580           return this._nodes.iterator()
102581         };
102582         PlanarGraph.prototype.linkResultDirectedEdges = function linkResultDirectedEdges () {
102583           for (var nodeit = this._nodes.iterator(); nodeit.hasNext();) {
102584             var node = nodeit.next();
102585             node.getEdges().linkResultDirectedEdges();
102586           }
102587         };
102588         PlanarGraph.prototype.debugPrintln = function debugPrintln (o) {
102589           System.out.println(o);
102590         };
102591         PlanarGraph.prototype.isBoundaryNode = function isBoundaryNode (geomIndex, coord) {
102592           var node = this._nodes.find(coord);
102593           if (node === null) { return false }
102594           var label = node.getLabel();
102595           if (label !== null && label.getLocation(geomIndex) === Location.BOUNDARY) { return true }
102596           return false
102597         };
102598         PlanarGraph.prototype.linkAllDirectedEdges = function linkAllDirectedEdges () {
102599           for (var nodeit = this._nodes.iterator(); nodeit.hasNext();) {
102600             var node = nodeit.next();
102601             node.getEdges().linkAllDirectedEdges();
102602           }
102603         };
102604         PlanarGraph.prototype.matchInSameDirection = function matchInSameDirection (p0, p1, ep0, ep1) {
102605           if (!p0.equals(ep0)) { return false }
102606           if (CGAlgorithms.computeOrientation(p0, p1, ep1) === CGAlgorithms.COLLINEAR && Quadrant.quadrant(p0, p1) === Quadrant.quadrant(ep0, ep1)) { return true }
102607           return false
102608         };
102609         PlanarGraph.prototype.getEdgeEnds = function getEdgeEnds () {
102610           return this._edgeEndList
102611         };
102612         PlanarGraph.prototype.debugPrint = function debugPrint (o) {
102613           System.out.print(o);
102614         };
102615         PlanarGraph.prototype.getEdgeIterator = function getEdgeIterator () {
102616           return this._edges.iterator()
102617         };
102618         PlanarGraph.prototype.findEdgeInSameDirection = function findEdgeInSameDirection (p0, p1) {
102619             var this$1 = this;
102620
102621           for (var i = 0; i < this._edges.size(); i++) {
102622             var e = this$1._edges.get(i);
102623             var eCoord = e.getCoordinates();
102624             if (this$1.matchInSameDirection(p0, p1, eCoord[0], eCoord[1])) { return e }
102625             if (this$1.matchInSameDirection(p0, p1, eCoord[eCoord.length - 1], eCoord[eCoord.length - 2])) { return e }
102626           }
102627           return null
102628         };
102629         PlanarGraph.prototype.insertEdge = function insertEdge (e) {
102630           this._edges.add(e);
102631         };
102632         PlanarGraph.prototype.findEdgeEnd = function findEdgeEnd (e) {
102633           for (var i = this.getEdgeEnds().iterator(); i.hasNext();) {
102634             var ee = i.next();
102635             if (ee.getEdge() === e) { return ee }
102636           }
102637           return null
102638         };
102639         PlanarGraph.prototype.addEdges = function addEdges (edgesToAdd) {
102640             var this$1 = this;
102641
102642           for (var it = edgesToAdd.iterator(); it.hasNext();) {
102643             var e = it.next();
102644             this$1._edges.add(e);
102645             var de1 = new DirectedEdge(e, true);
102646             var de2 = new DirectedEdge(e, false);
102647             de1.setSym(de2);
102648             de2.setSym(de1);
102649             this$1.add(de1);
102650             this$1.add(de2);
102651           }
102652         };
102653         PlanarGraph.prototype.add = function add (e) {
102654           this._nodes.add(e);
102655           this._edgeEndList.add(e);
102656         };
102657         PlanarGraph.prototype.getNodes = function getNodes () {
102658           return this._nodes.values()
102659         };
102660         PlanarGraph.prototype.findEdge = function findEdge (p0, p1) {
102661             var this$1 = this;
102662
102663           for (var i = 0; i < this._edges.size(); i++) {
102664             var e = this$1._edges.get(i);
102665             var eCoord = e.getCoordinates();
102666             if (p0.equals(eCoord[0]) && p1.equals(eCoord[1])) { return e }
102667           }
102668           return null
102669         };
102670         PlanarGraph.prototype.interfaces_ = function interfaces_ () {
102671           return []
102672         };
102673         PlanarGraph.prototype.getClass = function getClass () {
102674           return PlanarGraph
102675         };
102676         PlanarGraph.linkResultDirectedEdges = function linkResultDirectedEdges (nodes) {
102677           for (var nodeit = nodes.iterator(); nodeit.hasNext();) {
102678             var node = nodeit.next();
102679             node.getEdges().linkResultDirectedEdges();
102680           }
102681         };
102682
102683         var PolygonBuilder = function PolygonBuilder () {
102684           this._geometryFactory = null;
102685           this._shellList = new ArrayList();
102686           var geometryFactory = arguments[0];
102687           this._geometryFactory = geometryFactory;
102688         };
102689         PolygonBuilder.prototype.sortShellsAndHoles = function sortShellsAndHoles (edgeRings, shellList, freeHoleList) {
102690           for (var it = edgeRings.iterator(); it.hasNext();) {
102691             var er = it.next();
102692             if (er.isHole()) {
102693               freeHoleList.add(er);
102694             } else {
102695               shellList.add(er);
102696             }
102697           }
102698         };
102699         PolygonBuilder.prototype.computePolygons = function computePolygons (shellList) {
102700             var this$1 = this;
102701
102702           var resultPolyList = new ArrayList();
102703           for (var it = shellList.iterator(); it.hasNext();) {
102704             var er = it.next();
102705             var poly = er.toPolygon(this$1._geometryFactory);
102706             resultPolyList.add(poly);
102707           }
102708           return resultPolyList
102709         };
102710         PolygonBuilder.prototype.placeFreeHoles = function placeFreeHoles (shellList, freeHoleList) {
102711             var this$1 = this;
102712
102713           for (var it = freeHoleList.iterator(); it.hasNext();) {
102714             var hole = it.next();
102715             if (hole.getShell() === null) {
102716               var shell = this$1.findEdgeRingContaining(hole, shellList);
102717               if (shell === null) { throw new TopologyException('unable to assign hole to a shell', hole.getCoordinate(0)) }
102718               hole.setShell(shell);
102719             }
102720           }
102721         };
102722         PolygonBuilder.prototype.buildMinimalEdgeRings = function buildMinimalEdgeRings (maxEdgeRings, shellList, freeHoleList) {
102723             var this$1 = this;
102724
102725           var edgeRings = new ArrayList();
102726           for (var it = maxEdgeRings.iterator(); it.hasNext();) {
102727             var er = it.next();
102728             if (er.getMaxNodeDegree() > 2) {
102729               er.linkDirectedEdgesForMinimalEdgeRings();
102730               var minEdgeRings = er.buildMinimalRings();
102731               var shell = this$1.findShell(minEdgeRings);
102732               if (shell !== null) {
102733                 this$1.placePolygonHoles(shell, minEdgeRings);
102734                 shellList.add(shell);
102735               } else {
102736                 freeHoleList.addAll(minEdgeRings);
102737               }
102738             } else {
102739               edgeRings.add(er);
102740             }
102741           }
102742           return edgeRings
102743         };
102744         PolygonBuilder.prototype.containsPoint = function containsPoint (p) {
102745           for (var it = this._shellList.iterator(); it.hasNext();) {
102746             var er = it.next();
102747             if (er.containsPoint(p)) { return true }
102748           }
102749           return false
102750         };
102751         PolygonBuilder.prototype.buildMaximalEdgeRings = function buildMaximalEdgeRings (dirEdges) {
102752             var this$1 = this;
102753
102754           var maxEdgeRings = new ArrayList();
102755           for (var it = dirEdges.iterator(); it.hasNext();) {
102756             var de = it.next();
102757             if (de.isInResult() && de.getLabel().isArea()) {
102758               if (de.getEdgeRing() === null) {
102759                 var er = new MaximalEdgeRing(de, this$1._geometryFactory);
102760                 maxEdgeRings.add(er);
102761                 er.setInResult();
102762               }
102763             }
102764           }
102765           return maxEdgeRings
102766         };
102767         PolygonBuilder.prototype.placePolygonHoles = function placePolygonHoles (shell, minEdgeRings) {
102768           for (var it = minEdgeRings.iterator(); it.hasNext();) {
102769             var er = it.next();
102770             if (er.isHole()) {
102771               er.setShell(shell);
102772             }
102773           }
102774         };
102775         PolygonBuilder.prototype.getPolygons = function getPolygons () {
102776           var resultPolyList = this.computePolygons(this._shellList);
102777           return resultPolyList
102778         };
102779         PolygonBuilder.prototype.findEdgeRingContaining = function findEdgeRingContaining (testEr, shellList) {
102780           var testRing = testEr.getLinearRing();
102781           var testEnv = testRing.getEnvelopeInternal();
102782           var testPt = testRing.getCoordinateN(0);
102783           var minShell = null;
102784           var minEnv = null;
102785           for (var it = shellList.iterator(); it.hasNext();) {
102786             var tryShell = it.next();
102787             var tryRing = tryShell.getLinearRing();
102788             var tryEnv = tryRing.getEnvelopeInternal();
102789             if (minShell !== null) { minEnv = minShell.getLinearRing().getEnvelopeInternal(); }
102790             var isContained = false;
102791             if (tryEnv.contains(testEnv) && CGAlgorithms.isPointInRing(testPt, tryRing.getCoordinates())) { isContained = true; }
102792             if (isContained) {
102793               if (minShell === null || minEnv.contains(tryEnv)) {
102794                 minShell = tryShell;
102795               }
102796             }
102797           }
102798           return minShell
102799         };
102800         PolygonBuilder.prototype.findShell = function findShell (minEdgeRings) {
102801           var shellCount = 0;
102802           var shell = null;
102803           for (var it = minEdgeRings.iterator(); it.hasNext();) {
102804             var er = it.next();
102805             if (!er.isHole()) {
102806               shell = er;
102807               shellCount++;
102808             }
102809           }
102810           Assert.isTrue(shellCount <= 1, 'found two shells in MinimalEdgeRing list');
102811           return shell
102812         };
102813         PolygonBuilder.prototype.add = function add () {
102814           if (arguments.length === 1) {
102815             var graph = arguments[0];
102816             this.add(graph.getEdgeEnds(), graph.getNodes());
102817           } else if (arguments.length === 2) {
102818             var dirEdges = arguments[0];
102819             var nodes = arguments[1];
102820             PlanarGraph.linkResultDirectedEdges(nodes);
102821             var maxEdgeRings = this.buildMaximalEdgeRings(dirEdges);
102822             var freeHoleList = new ArrayList();
102823             var edgeRings = this.buildMinimalEdgeRings(maxEdgeRings, this._shellList, freeHoleList);
102824             this.sortShellsAndHoles(edgeRings, this._shellList, freeHoleList);
102825             this.placeFreeHoles(this._shellList, freeHoleList);
102826           }
102827         };
102828         PolygonBuilder.prototype.interfaces_ = function interfaces_ () {
102829           return []
102830         };
102831         PolygonBuilder.prototype.getClass = function getClass () {
102832           return PolygonBuilder
102833         };
102834
102835         var Boundable = function Boundable () {};
102836
102837         Boundable.prototype.getBounds = function getBounds () {};
102838         Boundable.prototype.interfaces_ = function interfaces_ () {
102839           return []
102840         };
102841         Boundable.prototype.getClass = function getClass () {
102842           return Boundable
102843         };
102844
102845         var ItemBoundable = function ItemBoundable () {
102846           this._bounds = null;
102847           this._item = null;
102848           var bounds = arguments[0];
102849           var item = arguments[1];
102850           this._bounds = bounds;
102851           this._item = item;
102852         };
102853         ItemBoundable.prototype.getItem = function getItem () {
102854           return this._item
102855         };
102856         ItemBoundable.prototype.getBounds = function getBounds () {
102857           return this._bounds
102858         };
102859         ItemBoundable.prototype.interfaces_ = function interfaces_ () {
102860           return [Boundable, Serializable]
102861         };
102862         ItemBoundable.prototype.getClass = function getClass () {
102863           return ItemBoundable
102864         };
102865
102866         var PriorityQueue = function PriorityQueue () {
102867           this._size = null;
102868           this._items = null;
102869           this._size = 0;
102870           this._items = new ArrayList();
102871           this._items.add(null);
102872         };
102873         PriorityQueue.prototype.poll = function poll () {
102874           if (this.isEmpty()) { return null }
102875           var minItem = this._items.get(1);
102876           this._items.set(1, this._items.get(this._size));
102877           this._size -= 1;
102878           this.reorder(1);
102879           return minItem
102880         };
102881         PriorityQueue.prototype.size = function size () {
102882           return this._size
102883         };
102884         PriorityQueue.prototype.reorder = function reorder (hole) {
102885             var this$1 = this;
102886
102887           var child = null;
102888           var tmp = this._items.get(hole);
102889           for (; hole * 2 <= this._size; hole = child) {
102890             child = hole * 2;
102891             if (child !== this$1._size && this$1._items.get(child + 1).compareTo(this$1._items.get(child)) < 0) { child++; }
102892             if (this$1._items.get(child).compareTo(tmp) < 0) { this$1._items.set(hole, this$1._items.get(child)); } else { break }
102893           }
102894           this._items.set(hole, tmp);
102895         };
102896         PriorityQueue.prototype.clear = function clear () {
102897           this._size = 0;
102898           this._items.clear();
102899         };
102900         PriorityQueue.prototype.isEmpty = function isEmpty () {
102901           return this._size === 0
102902         };
102903         PriorityQueue.prototype.add = function add (x) {
102904             var this$1 = this;
102905
102906           this._items.add(null);
102907           this._size += 1;
102908           var hole = this._size;
102909           this._items.set(0, x);
102910           for (; x.compareTo(this._items.get(Math.trunc(hole / 2))) < 0; hole /= 2) {
102911             this$1._items.set(hole, this$1._items.get(Math.trunc(hole / 2)));
102912           }
102913           this._items.set(hole, x);
102914         };
102915         PriorityQueue.prototype.interfaces_ = function interfaces_ () {
102916           return []
102917         };
102918         PriorityQueue.prototype.getClass = function getClass () {
102919           return PriorityQueue
102920         };
102921
102922         var ItemVisitor = function ItemVisitor () {};
102923
102924         ItemVisitor.prototype.visitItem = function visitItem (item) {};
102925         ItemVisitor.prototype.interfaces_ = function interfaces_ () {
102926           return []
102927         };
102928         ItemVisitor.prototype.getClass = function getClass () {
102929           return ItemVisitor
102930         };
102931
102932         var SpatialIndex = function SpatialIndex () {};
102933
102934         SpatialIndex.prototype.insert = function insert (itemEnv, item) {};
102935         SpatialIndex.prototype.remove = function remove (itemEnv, item) {};
102936         SpatialIndex.prototype.query = function query () {
102937           // if (arguments.length === 1) {
102938           // const searchEnv = arguments[0]
102939           // } else if (arguments.length === 2) {
102940           // const searchEnv = arguments[0]
102941           // const visitor = arguments[1]
102942           // }
102943         };
102944         SpatialIndex.prototype.interfaces_ = function interfaces_ () {
102945           return []
102946         };
102947         SpatialIndex.prototype.getClass = function getClass () {
102948           return SpatialIndex
102949         };
102950
102951         var AbstractNode = function AbstractNode () {
102952           this._childBoundables = new ArrayList();
102953           this._bounds = null;
102954           this._level = null;
102955           if (arguments.length === 0) ; else if (arguments.length === 1) {
102956             var level = arguments[0];
102957             this._level = level;
102958           }
102959         };
102960
102961         var staticAccessors$22 = { serialVersionUID: { configurable: true } };
102962         AbstractNode.prototype.getLevel = function getLevel () {
102963           return this._level
102964         };
102965         AbstractNode.prototype.size = function size () {
102966           return this._childBoundables.size()
102967         };
102968         AbstractNode.prototype.getChildBoundables = function getChildBoundables () {
102969           return this._childBoundables
102970         };
102971         AbstractNode.prototype.addChildBoundable = function addChildBoundable (childBoundable) {
102972           Assert.isTrue(this._bounds === null);
102973           this._childBoundables.add(childBoundable);
102974         };
102975         AbstractNode.prototype.isEmpty = function isEmpty () {
102976           return this._childBoundables.isEmpty()
102977         };
102978         AbstractNode.prototype.getBounds = function getBounds () {
102979           if (this._bounds === null) {
102980             this._bounds = this.computeBounds();
102981           }
102982           return this._bounds
102983         };
102984         AbstractNode.prototype.interfaces_ = function interfaces_ () {
102985           return [Boundable, Serializable]
102986         };
102987         AbstractNode.prototype.getClass = function getClass () {
102988           return AbstractNode
102989         };
102990         staticAccessors$22.serialVersionUID.get = function () { return 6493722185909573708 };
102991
102992         Object.defineProperties( AbstractNode, staticAccessors$22 );
102993
102994         var Collections = function Collections () {};
102995
102996         Collections.reverseOrder = function reverseOrder () {
102997           return {
102998             compare: function compare (a, b) {
102999               return b.compareTo(a)
103000             }
103001           }
103002         };
103003         Collections.min = function min (l) {
103004           Collections.sort(l);
103005           return l.get(0)
103006         };
103007         Collections.sort = function sort (l, c) {
103008           var a = l.toArray();
103009           if (c) {
103010             Arrays.sort(a, c);
103011           } else {
103012             Arrays.sort(a);
103013           }
103014           var i = l.iterator();
103015           for (var pos = 0, alen = a.length; pos < alen; pos++) {
103016             i.next();
103017             i.set(a[pos]);
103018           }
103019         };
103020         Collections.singletonList = function singletonList (o) {
103021           var arrayList = new ArrayList();
103022           arrayList.add(o);
103023           return arrayList
103024         };
103025
103026         var BoundablePair = function BoundablePair () {
103027           this._boundable1 = null;
103028           this._boundable2 = null;
103029           this._distance = null;
103030           this._itemDistance = null;
103031           var boundable1 = arguments[0];
103032           var boundable2 = arguments[1];
103033           var itemDistance = arguments[2];
103034           this._boundable1 = boundable1;
103035           this._boundable2 = boundable2;
103036           this._itemDistance = itemDistance;
103037           this._distance = this.distance();
103038         };
103039         BoundablePair.prototype.expandToQueue = function expandToQueue (priQ, minDistance) {
103040           var isComp1 = BoundablePair.isComposite(this._boundable1);
103041           var isComp2 = BoundablePair.isComposite(this._boundable2);
103042           if (isComp1 && isComp2) {
103043             if (BoundablePair.area(this._boundable1) > BoundablePair.area(this._boundable2)) {
103044               this.expand(this._boundable1, this._boundable2, priQ, minDistance);
103045               return null
103046             } else {
103047               this.expand(this._boundable2, this._boundable1, priQ, minDistance);
103048               return null
103049             }
103050           } else if (isComp1) {
103051             this.expand(this._boundable1, this._boundable2, priQ, minDistance);
103052             return null
103053           } else if (isComp2) {
103054             this.expand(this._boundable2, this._boundable1, priQ, minDistance);
103055             return null
103056           }
103057           throw new IllegalArgumentException('neither boundable is composite')
103058         };
103059         BoundablePair.prototype.isLeaves = function isLeaves () {
103060           return !(BoundablePair.isComposite(this._boundable1) || BoundablePair.isComposite(this._boundable2))
103061         };
103062         BoundablePair.prototype.compareTo = function compareTo (o) {
103063           var nd = o;
103064           if (this._distance < nd._distance) { return -1 }
103065           if (this._distance > nd._distance) { return 1 }
103066           return 0
103067         };
103068         BoundablePair.prototype.expand = function expand (bndComposite, bndOther, priQ, minDistance) {
103069             var this$1 = this;
103070
103071           var children = bndComposite.getChildBoundables();
103072           for (var i = children.iterator(); i.hasNext();) {
103073             var child = i.next();
103074             var bp = new BoundablePair(child, bndOther, this$1._itemDistance);
103075             if (bp.getDistance() < minDistance) {
103076               priQ.add(bp);
103077             }
103078           }
103079         };
103080         BoundablePair.prototype.getBoundable = function getBoundable (i) {
103081           if (i === 0) { return this._boundable1 }
103082           return this._boundable2
103083         };
103084         BoundablePair.prototype.getDistance = function getDistance () {
103085           return this._distance
103086         };
103087         BoundablePair.prototype.distance = function distance () {
103088           if (this.isLeaves()) {
103089             return this._itemDistance.distance(this._boundable1, this._boundable2)
103090           }
103091           return this._boundable1.getBounds().distance(this._boundable2.getBounds())
103092         };
103093         BoundablePair.prototype.interfaces_ = function interfaces_ () {
103094           return [Comparable]
103095         };
103096         BoundablePair.prototype.getClass = function getClass () {
103097           return BoundablePair
103098         };
103099         BoundablePair.area = function area (b) {
103100           return b.getBounds().getArea()
103101         };
103102         BoundablePair.isComposite = function isComposite (item) {
103103           return item instanceof AbstractNode
103104         };
103105
103106         var AbstractSTRtree = function AbstractSTRtree () {
103107           this._root = null;
103108           this._built = false;
103109           this._itemBoundables = new ArrayList();
103110           this._nodeCapacity = null;
103111           if (arguments.length === 0) {
103112             var nodeCapacity = AbstractSTRtree.DEFAULT_NODE_CAPACITY;
103113             this._nodeCapacity = nodeCapacity;
103114           } else if (arguments.length === 1) {
103115             var nodeCapacity$1 = arguments[0];
103116             Assert.isTrue(nodeCapacity$1 > 1, 'Node capacity must be greater than 1');
103117             this._nodeCapacity = nodeCapacity$1;
103118           }
103119         };
103120
103121         var staticAccessors$23 = { IntersectsOp: { configurable: true },serialVersionUID: { configurable: true },DEFAULT_NODE_CAPACITY: { configurable: true } };
103122         AbstractSTRtree.prototype.getNodeCapacity = function getNodeCapacity () {
103123           return this._nodeCapacity
103124         };
103125         AbstractSTRtree.prototype.lastNode = function lastNode (nodes) {
103126           return nodes.get(nodes.size() - 1)
103127         };
103128         AbstractSTRtree.prototype.size = function size () {
103129             var this$1 = this;
103130
103131           if (arguments.length === 0) {
103132             if (this.isEmpty()) {
103133               return 0
103134             }
103135             this.build();
103136             return this.size(this._root)
103137           } else if (arguments.length === 1) {
103138             var node = arguments[0];
103139             var size = 0;
103140             for (var i = node.getChildBoundables().iterator(); i.hasNext();) {
103141               var childBoundable = i.next();
103142               if (childBoundable instanceof AbstractNode) {
103143                 size += this$1.size(childBoundable);
103144               } else if (childBoundable instanceof ItemBoundable) {
103145                 size += 1;
103146               }
103147             }
103148             return size
103149           }
103150         };
103151         AbstractSTRtree.prototype.removeItem = function removeItem (node, item) {
103152           var childToRemove = null;
103153           for (var i = node.getChildBoundables().iterator(); i.hasNext();) {
103154             var childBoundable = i.next();
103155             if (childBoundable instanceof ItemBoundable) {
103156               if (childBoundable.getItem() === item) { childToRemove = childBoundable; }
103157             }
103158           }
103159           if (childToRemove !== null) {
103160             node.getChildBoundables().remove(childToRemove);
103161             return true
103162           }
103163           return false
103164         };
103165         AbstractSTRtree.prototype.itemsTree = function itemsTree () {
103166             var this$1 = this;
103167
103168           if (arguments.length === 0) {
103169             this.build();
103170             var valuesTree = this.itemsTree(this._root);
103171             if (valuesTree === null) { return new ArrayList() }
103172             return valuesTree
103173           } else if (arguments.length === 1) {
103174             var node = arguments[0];
103175             var valuesTreeForNode = new ArrayList();
103176             for (var i = node.getChildBoundables().iterator(); i.hasNext();) {
103177               var childBoundable = i.next();
103178               if (childBoundable instanceof AbstractNode) {
103179                 var valuesTreeForChild = this$1.itemsTree(childBoundable);
103180                 if (valuesTreeForChild !== null) { valuesTreeForNode.add(valuesTreeForChild); }
103181               } else if (childBoundable instanceof ItemBoundable) {
103182                 valuesTreeForNode.add(childBoundable.getItem());
103183               } else {
103184                 Assert.shouldNeverReachHere();
103185               }
103186             }
103187             if (valuesTreeForNode.size() <= 0) { return null }
103188             return valuesTreeForNode
103189           }
103190         };
103191         AbstractSTRtree.prototype.insert = function insert (bounds, item) {
103192           Assert.isTrue(!this._built, 'Cannot insert items into an STR packed R-tree after it has been built.');
103193           this._itemBoundables.add(new ItemBoundable(bounds, item));
103194         };
103195         AbstractSTRtree.prototype.boundablesAtLevel = function boundablesAtLevel () {
103196             var this$1 = this;
103197
103198           if (arguments.length === 1) {
103199             var level = arguments[0];
103200             var boundables = new ArrayList();
103201             this.boundablesAtLevel(level, this._root, boundables);
103202             return boundables
103203           } else if (arguments.length === 3) {
103204             var level$1 = arguments[0];
103205             var top = arguments[1];
103206             var boundables$1 = arguments[2];
103207             Assert.isTrue(level$1 > -2);
103208             if (top.getLevel() === level$1) {
103209               boundables$1.add(top);
103210               return null
103211             }
103212             for (var i = top.getChildBoundables().iterator(); i.hasNext();) {
103213               var boundable = i.next();
103214               if (boundable instanceof AbstractNode) {
103215                 this$1.boundablesAtLevel(level$1, boundable, boundables$1);
103216               } else {
103217                 Assert.isTrue(boundable instanceof ItemBoundable);
103218                 if (level$1 === -1) {
103219                   boundables$1.add(boundable);
103220                 }
103221               }
103222             }
103223             return null
103224           }
103225         };
103226         AbstractSTRtree.prototype.query = function query () {
103227             var this$1 = this;
103228
103229           if (arguments.length === 1) {
103230             var searchBounds = arguments[0];
103231             this.build();
103232             var matches = new ArrayList();
103233             if (this.isEmpty()) {
103234               return matches
103235             }
103236             if (this.getIntersectsOp().intersects(this._root.getBounds(), searchBounds)) {
103237               this.query(searchBounds, this._root, matches);
103238             }
103239             return matches
103240           } else if (arguments.length === 2) {
103241             var searchBounds$1 = arguments[0];
103242             var visitor = arguments[1];
103243             this.build();
103244             if (this.isEmpty()) {
103245               return null
103246             }
103247             if (this.getIntersectsOp().intersects(this._root.getBounds(), searchBounds$1)) {
103248               this.query(searchBounds$1, this._root, visitor);
103249             }
103250           } else if (arguments.length === 3) {
103251             if (hasInterface(arguments[2], ItemVisitor) && (arguments[0] instanceof Object && arguments[1] instanceof AbstractNode)) {
103252               var searchBounds$2 = arguments[0];
103253               var node = arguments[1];
103254               var visitor$1 = arguments[2];
103255               var childBoundables = node.getChildBoundables();
103256               for (var i = 0; i < childBoundables.size(); i++) {
103257                 var childBoundable = childBoundables.get(i);
103258                 if (!this$1.getIntersectsOp().intersects(childBoundable.getBounds(), searchBounds$2)) {
103259                   continue
103260                 }
103261                 if (childBoundable instanceof AbstractNode) {
103262                   this$1.query(searchBounds$2, childBoundable, visitor$1);
103263                 } else if (childBoundable instanceof ItemBoundable) {
103264                   visitor$1.visitItem(childBoundable.getItem());
103265                 } else {
103266                   Assert.shouldNeverReachHere();
103267                 }
103268               }
103269             } else if (hasInterface(arguments[2], List) && (arguments[0] instanceof Object && arguments[1] instanceof AbstractNode)) {
103270               var searchBounds$3 = arguments[0];
103271               var node$1 = arguments[1];
103272               var matches$1 = arguments[2];
103273               var childBoundables$1 = node$1.getChildBoundables();
103274               for (var i$1 = 0; i$1 < childBoundables$1.size(); i$1++) {
103275                 var childBoundable$1 = childBoundables$1.get(i$1);
103276                 if (!this$1.getIntersectsOp().intersects(childBoundable$1.getBounds(), searchBounds$3)) {
103277                   continue
103278                 }
103279                 if (childBoundable$1 instanceof AbstractNode) {
103280                   this$1.query(searchBounds$3, childBoundable$1, matches$1);
103281                 } else if (childBoundable$1 instanceof ItemBoundable) {
103282                   matches$1.add(childBoundable$1.getItem());
103283                 } else {
103284                   Assert.shouldNeverReachHere();
103285                 }
103286               }
103287             }
103288           }
103289         };
103290         AbstractSTRtree.prototype.build = function build () {
103291           if (this._built) { return null }
103292           this._root = this._itemBoundables.isEmpty() ? this.createNode(0) : this.createHigherLevels(this._itemBoundables, -1);
103293           this._itemBoundables = null;
103294           this._built = true;
103295         };
103296         AbstractSTRtree.prototype.getRoot = function getRoot () {
103297           this.build();
103298           return this._root
103299         };
103300         AbstractSTRtree.prototype.remove = function remove () {
103301             var this$1 = this;
103302
103303           if (arguments.length === 2) {
103304             var searchBounds = arguments[0];
103305             var item = arguments[1];
103306             this.build();
103307             if (this.getIntersectsOp().intersects(this._root.getBounds(), searchBounds)) {
103308               return this.remove(searchBounds, this._root, item)
103309             }
103310             return false
103311           } else if (arguments.length === 3) {
103312             var searchBounds$1 = arguments[0];
103313             var node = arguments[1];
103314             var item$1 = arguments[2];
103315             var found = this.removeItem(node, item$1);
103316             if (found) { return true }
103317             var childToPrune = null;
103318             for (var i = node.getChildBoundables().iterator(); i.hasNext();) {
103319               var childBoundable = i.next();
103320               if (!this$1.getIntersectsOp().intersects(childBoundable.getBounds(), searchBounds$1)) {
103321                 continue
103322               }
103323               if (childBoundable instanceof AbstractNode) {
103324                 found = this$1.remove(searchBounds$1, childBoundable, item$1);
103325                 if (found) {
103326                   childToPrune = childBoundable;
103327                   break
103328                 }
103329               }
103330             }
103331             if (childToPrune !== null) {
103332               if (childToPrune.getChildBoundables().isEmpty()) {
103333                 node.getChildBoundables().remove(childToPrune);
103334               }
103335             }
103336             return found
103337           }
103338         };
103339         AbstractSTRtree.prototype.createHigherLevels = function createHigherLevels (boundablesOfALevel, level) {
103340           Assert.isTrue(!boundablesOfALevel.isEmpty());
103341           var parentBoundables = this.createParentBoundables(boundablesOfALevel, level + 1);
103342           if (parentBoundables.size() === 1) {
103343             return parentBoundables.get(0)
103344           }
103345           return this.createHigherLevels(parentBoundables, level + 1)
103346         };
103347         AbstractSTRtree.prototype.depth = function depth () {
103348             var this$1 = this;
103349
103350           if (arguments.length === 0) {
103351             if (this.isEmpty()) {
103352               return 0
103353             }
103354             this.build();
103355             return this.depth(this._root)
103356           } else if (arguments.length === 1) {
103357             var node = arguments[0];
103358             var maxChildDepth = 0;
103359             for (var i = node.getChildBoundables().iterator(); i.hasNext();) {
103360               var childBoundable = i.next();
103361               if (childBoundable instanceof AbstractNode) {
103362                 var childDepth = this$1.depth(childBoundable);
103363                 if (childDepth > maxChildDepth) { maxChildDepth = childDepth; }
103364               }
103365             }
103366             return maxChildDepth + 1
103367           }
103368         };
103369         AbstractSTRtree.prototype.createParentBoundables = function createParentBoundables (childBoundables, newLevel) {
103370             var this$1 = this;
103371
103372           Assert.isTrue(!childBoundables.isEmpty());
103373           var parentBoundables = new ArrayList();
103374           parentBoundables.add(this.createNode(newLevel));
103375           var sortedChildBoundables = new ArrayList(childBoundables);
103376           Collections.sort(sortedChildBoundables, this.getComparator());
103377           for (var i = sortedChildBoundables.iterator(); i.hasNext();) {
103378             var childBoundable = i.next();
103379             if (this$1.lastNode(parentBoundables).getChildBoundables().size() === this$1.getNodeCapacity()) {
103380               parentBoundables.add(this$1.createNode(newLevel));
103381             }
103382             this$1.lastNode(parentBoundables).addChildBoundable(childBoundable);
103383           }
103384           return parentBoundables
103385         };
103386         AbstractSTRtree.prototype.isEmpty = function isEmpty () {
103387           if (!this._built) { return this._itemBoundables.isEmpty() }
103388           return this._root.isEmpty()
103389         };
103390         AbstractSTRtree.prototype.interfaces_ = function interfaces_ () {
103391           return [Serializable]
103392         };
103393         AbstractSTRtree.prototype.getClass = function getClass () {
103394           return AbstractSTRtree
103395         };
103396         AbstractSTRtree.compareDoubles = function compareDoubles (a, b) {
103397           return a > b ? 1 : a < b ? -1 : 0
103398         };
103399         staticAccessors$23.IntersectsOp.get = function () { return IntersectsOp };
103400         staticAccessors$23.serialVersionUID.get = function () { return -3886435814360241337 };
103401         staticAccessors$23.DEFAULT_NODE_CAPACITY.get = function () { return 10 };
103402
103403         Object.defineProperties( AbstractSTRtree, staticAccessors$23 );
103404
103405         var IntersectsOp = function IntersectsOp () {};
103406
103407         var ItemDistance = function ItemDistance () {};
103408
103409         ItemDistance.prototype.distance = function distance (item1, item2) {};
103410         ItemDistance.prototype.interfaces_ = function interfaces_ () {
103411           return []
103412         };
103413         ItemDistance.prototype.getClass = function getClass () {
103414           return ItemDistance
103415         };
103416
103417         var STRtree = (function (AbstractSTRtree$$1) {
103418           function STRtree (nodeCapacity) {
103419             nodeCapacity = nodeCapacity || STRtree.DEFAULT_NODE_CAPACITY;
103420             AbstractSTRtree$$1.call(this, nodeCapacity);
103421           }
103422
103423           if ( AbstractSTRtree$$1 ) { STRtree.__proto__ = AbstractSTRtree$$1; }
103424           STRtree.prototype = Object.create( AbstractSTRtree$$1 && AbstractSTRtree$$1.prototype );
103425           STRtree.prototype.constructor = STRtree;
103426
103427           var staticAccessors = { STRtreeNode: { configurable: true },serialVersionUID: { configurable: true },xComparator: { configurable: true },yComparator: { configurable: true },intersectsOp: { configurable: true },DEFAULT_NODE_CAPACITY: { configurable: true } };
103428           STRtree.prototype.createParentBoundablesFromVerticalSlices = function createParentBoundablesFromVerticalSlices (verticalSlices, newLevel) {
103429             var this$1 = this;
103430
103431             Assert.isTrue(verticalSlices.length > 0);
103432             var parentBoundables = new ArrayList();
103433             for (var i = 0; i < verticalSlices.length; i++) {
103434               parentBoundables.addAll(this$1.createParentBoundablesFromVerticalSlice(verticalSlices[i], newLevel));
103435             }
103436             return parentBoundables
103437           };
103438           STRtree.prototype.createNode = function createNode (level) {
103439             return new STRtreeNode(level)
103440           };
103441           STRtree.prototype.size = function size () {
103442             if (arguments.length === 0) {
103443               return AbstractSTRtree$$1.prototype.size.call(this)
103444             } else { return AbstractSTRtree$$1.prototype.size.apply(this, arguments) }
103445           };
103446           STRtree.prototype.insert = function insert () {
103447             if (arguments.length === 2) {
103448               var itemEnv = arguments[0];
103449               var item = arguments[1];
103450               if (itemEnv.isNull()) {
103451                 return null
103452               }
103453               AbstractSTRtree$$1.prototype.insert.call(this, itemEnv, item);
103454             } else { return AbstractSTRtree$$1.prototype.insert.apply(this, arguments) }
103455           };
103456           STRtree.prototype.getIntersectsOp = function getIntersectsOp () {
103457             return STRtree.intersectsOp
103458           };
103459           STRtree.prototype.verticalSlices = function verticalSlices (childBoundables, sliceCount) {
103460             var sliceCapacity = Math.trunc(Math.ceil(childBoundables.size() / sliceCount));
103461             var slices = new Array(sliceCount).fill(null);
103462             var i = childBoundables.iterator();
103463             for (var j = 0; j < sliceCount; j++) {
103464               slices[j] = new ArrayList();
103465               var boundablesAddedToSlice = 0;
103466               while (i.hasNext() && boundablesAddedToSlice < sliceCapacity) {
103467                 var childBoundable = i.next();
103468                 slices[j].add(childBoundable);
103469                 boundablesAddedToSlice++;
103470               }
103471             }
103472             return slices
103473           };
103474           STRtree.prototype.query = function query () {
103475             if (arguments.length === 1) {
103476               var searchEnv = arguments[0];
103477               return AbstractSTRtree$$1.prototype.query.call(this, searchEnv)
103478             } else if (arguments.length === 2) {
103479               var searchEnv$1 = arguments[0];
103480               var visitor = arguments[1];
103481               AbstractSTRtree$$1.prototype.query.call(this, searchEnv$1, visitor);
103482             } else if (arguments.length === 3) {
103483               if (hasInterface(arguments[2], ItemVisitor) && (arguments[0] instanceof Object && arguments[1] instanceof AbstractNode)) {
103484                 var searchBounds = arguments[0];
103485                 var node = arguments[1];
103486                 var visitor$1 = arguments[2];
103487                 AbstractSTRtree$$1.prototype.query.call(this, searchBounds, node, visitor$1);
103488               } else if (hasInterface(arguments[2], List) && (arguments[0] instanceof Object && arguments[1] instanceof AbstractNode)) {
103489                 var searchBounds$1 = arguments[0];
103490                 var node$1 = arguments[1];
103491                 var matches = arguments[2];
103492                 AbstractSTRtree$$1.prototype.query.call(this, searchBounds$1, node$1, matches);
103493               }
103494             }
103495           };
103496           STRtree.prototype.getComparator = function getComparator () {
103497             return STRtree.yComparator
103498           };
103499           STRtree.prototype.createParentBoundablesFromVerticalSlice = function createParentBoundablesFromVerticalSlice (childBoundables, newLevel) {
103500             return AbstractSTRtree$$1.prototype.createParentBoundables.call(this, childBoundables, newLevel)
103501           };
103502           STRtree.prototype.remove = function remove () {
103503             if (arguments.length === 2) {
103504               var itemEnv = arguments[0];
103505               var item = arguments[1];
103506               return AbstractSTRtree$$1.prototype.remove.call(this, itemEnv, item)
103507             } else { return AbstractSTRtree$$1.prototype.remove.apply(this, arguments) }
103508           };
103509           STRtree.prototype.depth = function depth () {
103510             if (arguments.length === 0) {
103511               return AbstractSTRtree$$1.prototype.depth.call(this)
103512             } else { return AbstractSTRtree$$1.prototype.depth.apply(this, arguments) }
103513           };
103514           STRtree.prototype.createParentBoundables = function createParentBoundables (childBoundables, newLevel) {
103515             Assert.isTrue(!childBoundables.isEmpty());
103516             var minLeafCount = Math.trunc(Math.ceil(childBoundables.size() / this.getNodeCapacity()));
103517             var sortedChildBoundables = new ArrayList(childBoundables);
103518             Collections.sort(sortedChildBoundables, STRtree.xComparator);
103519             var verticalSlices = this.verticalSlices(sortedChildBoundables, Math.trunc(Math.ceil(Math.sqrt(minLeafCount))));
103520             return this.createParentBoundablesFromVerticalSlices(verticalSlices, newLevel)
103521           };
103522           STRtree.prototype.nearestNeighbour = function nearestNeighbour () {
103523             if (arguments.length === 1) {
103524               if (hasInterface(arguments[0], ItemDistance)) {
103525                 var itemDist = arguments[0];
103526                 var bp = new BoundablePair(this.getRoot(), this.getRoot(), itemDist);
103527                 return this.nearestNeighbour(bp)
103528               } else if (arguments[0] instanceof BoundablePair) {
103529                 var initBndPair = arguments[0];
103530                 return this.nearestNeighbour(initBndPair, Double.POSITIVE_INFINITY)
103531               }
103532             } else if (arguments.length === 2) {
103533               if (arguments[0] instanceof STRtree && hasInterface(arguments[1], ItemDistance)) {
103534                 var tree = arguments[0];
103535                 var itemDist$1 = arguments[1];
103536                 var bp$1 = new BoundablePair(this.getRoot(), tree.getRoot(), itemDist$1);
103537                 return this.nearestNeighbour(bp$1)
103538               } else if (arguments[0] instanceof BoundablePair && typeof arguments[1] === 'number') {
103539                 var initBndPair$1 = arguments[0];
103540                 var maxDistance = arguments[1];
103541                 var distanceLowerBound = maxDistance;
103542                 var minPair = null;
103543                 var priQ = new PriorityQueue();
103544                 priQ.add(initBndPair$1);
103545                 while (!priQ.isEmpty() && distanceLowerBound > 0.0) {
103546                   var bndPair = priQ.poll();
103547                   var currentDistance = bndPair.getDistance();
103548                   if (currentDistance >= distanceLowerBound) { break }
103549                   if (bndPair.isLeaves()) {
103550                     distanceLowerBound = currentDistance;
103551                     minPair = bndPair;
103552                   } else {
103553                     bndPair.expandToQueue(priQ, distanceLowerBound);
103554                   }
103555                 }
103556                 return [minPair.getBoundable(0).getItem(), minPair.getBoundable(1).getItem()]
103557               }
103558             } else if (arguments.length === 3) {
103559               var env = arguments[0];
103560               var item = arguments[1];
103561               var itemDist$2 = arguments[2];
103562               var bnd = new ItemBoundable(env, item);
103563               var bp$2 = new BoundablePair(this.getRoot(), bnd, itemDist$2);
103564               return this.nearestNeighbour(bp$2)[0]
103565             }
103566           };
103567           STRtree.prototype.interfaces_ = function interfaces_ () {
103568             return [SpatialIndex, Serializable]
103569           };
103570           STRtree.prototype.getClass = function getClass () {
103571             return STRtree
103572           };
103573           STRtree.centreX = function centreX (e) {
103574             return STRtree.avg(e.getMinX(), e.getMaxX())
103575           };
103576           STRtree.avg = function avg (a, b) {
103577             return (a + b) / 2
103578           };
103579           STRtree.centreY = function centreY (e) {
103580             return STRtree.avg(e.getMinY(), e.getMaxY())
103581           };
103582           staticAccessors.STRtreeNode.get = function () { return STRtreeNode };
103583           staticAccessors.serialVersionUID.get = function () { return 259274702368956900 };
103584           staticAccessors.xComparator.get = function () {
103585             return {
103586               interfaces_: function () {
103587                 return [Comparator]
103588               },
103589               compare: function (o1, o2) {
103590                 return AbstractSTRtree$$1.compareDoubles(STRtree.centreX(o1.getBounds()), STRtree.centreX(o2.getBounds()))
103591               }
103592             }
103593           };
103594           staticAccessors.yComparator.get = function () {
103595             return {
103596               interfaces_: function () {
103597                 return [Comparator]
103598               },
103599               compare: function (o1, o2) {
103600                 return AbstractSTRtree$$1.compareDoubles(STRtree.centreY(o1.getBounds()), STRtree.centreY(o2.getBounds()))
103601               }
103602             }
103603           };
103604           staticAccessors.intersectsOp.get = function () {
103605             return {
103606               interfaces_: function () {
103607                 return [AbstractSTRtree$$1.IntersectsOp]
103608               },
103609               intersects: function (aBounds, bBounds) {
103610                 return aBounds.intersects(bBounds)
103611               }
103612             }
103613           };
103614           staticAccessors.DEFAULT_NODE_CAPACITY.get = function () { return 10 };
103615
103616           Object.defineProperties( STRtree, staticAccessors );
103617
103618           return STRtree;
103619         }(AbstractSTRtree));
103620
103621         var STRtreeNode = (function (AbstractNode$$1) {
103622           function STRtreeNode () {
103623             var level = arguments[0];
103624             AbstractNode$$1.call(this, level);
103625           }
103626
103627           if ( AbstractNode$$1 ) { STRtreeNode.__proto__ = AbstractNode$$1; }
103628           STRtreeNode.prototype = Object.create( AbstractNode$$1 && AbstractNode$$1.prototype );
103629           STRtreeNode.prototype.constructor = STRtreeNode;
103630           STRtreeNode.prototype.computeBounds = function computeBounds () {
103631             var bounds = null;
103632             for (var i = this.getChildBoundables().iterator(); i.hasNext();) {
103633               var childBoundable = i.next();
103634               if (bounds === null) {
103635                 bounds = new Envelope(childBoundable.getBounds());
103636               } else {
103637                 bounds.expandToInclude(childBoundable.getBounds());
103638               }
103639             }
103640             return bounds
103641           };
103642           STRtreeNode.prototype.interfaces_ = function interfaces_ () {
103643             return []
103644           };
103645           STRtreeNode.prototype.getClass = function getClass () {
103646             return STRtreeNode
103647           };
103648
103649           return STRtreeNode;
103650         }(AbstractNode));
103651
103652         var SegmentPointComparator = function SegmentPointComparator () {};
103653
103654         SegmentPointComparator.prototype.interfaces_ = function interfaces_ () {
103655           return []
103656         };
103657         SegmentPointComparator.prototype.getClass = function getClass () {
103658           return SegmentPointComparator
103659         };
103660         SegmentPointComparator.relativeSign = function relativeSign (x0, x1) {
103661           if (x0 < x1) { return -1 }
103662           if (x0 > x1) { return 1 }
103663           return 0
103664         };
103665         SegmentPointComparator.compare = function compare (octant, p0, p1) {
103666           if (p0.equals2D(p1)) { return 0 }
103667           var xSign = SegmentPointComparator.relativeSign(p0.x, p1.x);
103668           var ySign = SegmentPointComparator.relativeSign(p0.y, p1.y);
103669           switch (octant) {
103670             case 0:
103671               return SegmentPointComparator.compareValue(xSign, ySign)
103672             case 1:
103673               return SegmentPointComparator.compareValue(ySign, xSign)
103674             case 2:
103675               return SegmentPointComparator.compareValue(ySign, -xSign)
103676             case 3:
103677               return SegmentPointComparator.compareValue(-xSign, ySign)
103678             case 4:
103679               return SegmentPointComparator.compareValue(-xSign, -ySign)
103680             case 5:
103681               return SegmentPointComparator.compareValue(-ySign, -xSign)
103682             case 6:
103683               return SegmentPointComparator.compareValue(-ySign, xSign)
103684             case 7:
103685               return SegmentPointComparator.compareValue(xSign, -ySign)
103686           }
103687           Assert.shouldNeverReachHere('invalid octant value');
103688           return 0
103689         };
103690         SegmentPointComparator.compareValue = function compareValue (compareSign0, compareSign1) {
103691           if (compareSign0 < 0) { return -1 }
103692           if (compareSign0 > 0) { return 1 }
103693           if (compareSign1 < 0) { return -1 }
103694           if (compareSign1 > 0) { return 1 }
103695           return 0
103696         };
103697
103698         var SegmentNode = function SegmentNode () {
103699           this._segString = null;
103700           this.coord = null;
103701           this.segmentIndex = null;
103702           this._segmentOctant = null;
103703           this._isInterior = null;
103704           var segString = arguments[0];
103705           var coord = arguments[1];
103706           var segmentIndex = arguments[2];
103707           var segmentOctant = arguments[3];
103708           this._segString = segString;
103709           this.coord = new Coordinate(coord);
103710           this.segmentIndex = segmentIndex;
103711           this._segmentOctant = segmentOctant;
103712           this._isInterior = !coord.equals2D(segString.getCoordinate(segmentIndex));
103713         };
103714         SegmentNode.prototype.getCoordinate = function getCoordinate () {
103715           return this.coord
103716         };
103717         SegmentNode.prototype.print = function print (out) {
103718           out.print(this.coord);
103719           out.print(' seg # = ' + this.segmentIndex);
103720         };
103721         SegmentNode.prototype.compareTo = function compareTo (obj) {
103722           var other = obj;
103723           if (this.segmentIndex < other.segmentIndex) { return -1 }
103724           if (this.segmentIndex > other.segmentIndex) { return 1 }
103725           if (this.coord.equals2D(other.coord)) { return 0 }
103726           return SegmentPointComparator.compare(this._segmentOctant, this.coord, other.coord)
103727         };
103728         SegmentNode.prototype.isEndPoint = function isEndPoint (maxSegmentIndex) {
103729           if (this.segmentIndex === 0 && !this._isInterior) { return true }
103730           if (this.segmentIndex === maxSegmentIndex) { return true }
103731           return false
103732         };
103733         SegmentNode.prototype.isInterior = function isInterior () {
103734           return this._isInterior
103735         };
103736         SegmentNode.prototype.interfaces_ = function interfaces_ () {
103737           return [Comparable]
103738         };
103739         SegmentNode.prototype.getClass = function getClass () {
103740           return SegmentNode
103741         };
103742
103743         // import Iterator from '../../../../java/util/Iterator'
103744         var SegmentNodeList = function SegmentNodeList () {
103745           this._nodeMap = new TreeMap();
103746           this._edge = null;
103747           var edge = arguments[0];
103748           this._edge = edge;
103749         };
103750         SegmentNodeList.prototype.getSplitCoordinates = function getSplitCoordinates () {
103751             var this$1 = this;
103752
103753           var coordList = new CoordinateList();
103754           this.addEndpoints();
103755           var it = this.iterator();
103756           var eiPrev = it.next();
103757           while (it.hasNext()) {
103758             var ei = it.next();
103759             this$1.addEdgeCoordinates(eiPrev, ei, coordList);
103760             eiPrev = ei;
103761           }
103762           return coordList.toCoordinateArray()
103763         };
103764         SegmentNodeList.prototype.addCollapsedNodes = function addCollapsedNodes () {
103765             var this$1 = this;
103766
103767           var collapsedVertexIndexes = new ArrayList();
103768           this.findCollapsesFromInsertedNodes(collapsedVertexIndexes);
103769           this.findCollapsesFromExistingVertices(collapsedVertexIndexes);
103770           for (var it = collapsedVertexIndexes.iterator(); it.hasNext();) {
103771             var vertexIndex = it.next().intValue();
103772             this$1.add(this$1._edge.getCoordinate(vertexIndex), vertexIndex);
103773           }
103774         };
103775         SegmentNodeList.prototype.print = function print (out) {
103776           out.println('Intersections:');
103777           for (var it = this.iterator(); it.hasNext();) {
103778             var ei = it.next();
103779             ei.print(out);
103780           }
103781         };
103782         SegmentNodeList.prototype.findCollapsesFromExistingVertices = function findCollapsesFromExistingVertices (collapsedVertexIndexes) {
103783             var this$1 = this;
103784
103785           for (var i = 0; i < this._edge.size() - 2; i++) {
103786             var p0 = this$1._edge.getCoordinate(i);
103787             // const p1 = this._edge.getCoordinate(i + 1)
103788             var p2 = this$1._edge.getCoordinate(i + 2);
103789             if (p0.equals2D(p2)) {
103790               collapsedVertexIndexes.add(new Integer(i + 1));
103791             }
103792           }
103793         };
103794         SegmentNodeList.prototype.addEdgeCoordinates = function addEdgeCoordinates (ei0, ei1, coordList) {
103795             var this$1 = this;
103796
103797           // let npts = ei1.segmentIndex - ei0.segmentIndex + 2
103798           var lastSegStartPt = this._edge.getCoordinate(ei1.segmentIndex);
103799           var useIntPt1 = ei1.isInterior() || !ei1.coord.equals2D(lastSegStartPt);
103800           // if (!useIntPt1) {
103801           // npts--
103802           // }
103803           // const ipt = 0
103804           coordList.add(new Coordinate(ei0.coord), false);
103805           for (var i = ei0.segmentIndex + 1; i <= ei1.segmentIndex; i++) {
103806             coordList.add(this$1._edge.getCoordinate(i));
103807           }
103808           if (useIntPt1) {
103809             coordList.add(new Coordinate(ei1.coord));
103810           }
103811         };
103812         SegmentNodeList.prototype.iterator = function iterator () {
103813           return this._nodeMap.values().iterator()
103814         };
103815         SegmentNodeList.prototype.addSplitEdges = function addSplitEdges (edgeList) {
103816             var this$1 = this;
103817
103818           this.addEndpoints();
103819           this.addCollapsedNodes();
103820           var it = this.iterator();
103821           var eiPrev = it.next();
103822           while (it.hasNext()) {
103823             var ei = it.next();
103824             var newEdge = this$1.createSplitEdge(eiPrev, ei);
103825             edgeList.add(newEdge);
103826             eiPrev = ei;
103827           }
103828         };
103829         SegmentNodeList.prototype.findCollapseIndex = function findCollapseIndex (ei0, ei1, collapsedVertexIndex) {
103830           if (!ei0.coord.equals2D(ei1.coord)) { return false }
103831           var numVerticesBetween = ei1.segmentIndex - ei0.segmentIndex;
103832           if (!ei1.isInterior()) {
103833             numVerticesBetween--;
103834           }
103835           if (numVerticesBetween === 1) {
103836             collapsedVertexIndex[0] = ei0.segmentIndex + 1;
103837             return true
103838           }
103839           return false
103840         };
103841         SegmentNodeList.prototype.findCollapsesFromInsertedNodes = function findCollapsesFromInsertedNodes (collapsedVertexIndexes) {
103842             var this$1 = this;
103843
103844           var collapsedVertexIndex = new Array(1).fill(null);
103845           var it = this.iterator();
103846           var eiPrev = it.next();
103847           while (it.hasNext()) {
103848             var ei = it.next();
103849             var isCollapsed = this$1.findCollapseIndex(eiPrev, ei, collapsedVertexIndex);
103850             if (isCollapsed) { collapsedVertexIndexes.add(new Integer(collapsedVertexIndex[0])); }
103851             eiPrev = ei;
103852           }
103853         };
103854         SegmentNodeList.prototype.getEdge = function getEdge () {
103855           return this._edge
103856         };
103857         SegmentNodeList.prototype.addEndpoints = function addEndpoints () {
103858           var maxSegIndex = this._edge.size() - 1;
103859           this.add(this._edge.getCoordinate(0), 0);
103860           this.add(this._edge.getCoordinate(maxSegIndex), maxSegIndex);
103861         };
103862         SegmentNodeList.prototype.createSplitEdge = function createSplitEdge (ei0, ei1) {
103863             var this$1 = this;
103864
103865           var npts = ei1.segmentIndex - ei0.segmentIndex + 2;
103866           var lastSegStartPt = this._edge.getCoordinate(ei1.segmentIndex);
103867           var useIntPt1 = ei1.isInterior() || !ei1.coord.equals2D(lastSegStartPt);
103868           if (!useIntPt1) {
103869             npts--;
103870           }
103871           var pts = new Array(npts).fill(null);
103872           var ipt = 0;
103873           pts[ipt++] = new Coordinate(ei0.coord);
103874           for (var i = ei0.segmentIndex + 1; i <= ei1.segmentIndex; i++) {
103875             pts[ipt++] = this$1._edge.getCoordinate(i);
103876           }
103877           if (useIntPt1) { pts[ipt] = new Coordinate(ei1.coord); }
103878           return new NodedSegmentString(pts, this._edge.getData())
103879         };
103880         SegmentNodeList.prototype.add = function add (intPt, segmentIndex) {
103881           var eiNew = new SegmentNode(this._edge, intPt, segmentIndex, this._edge.getSegmentOctant(segmentIndex));
103882           var ei = this._nodeMap.get(eiNew);
103883           if (ei !== null) {
103884             Assert.isTrue(ei.coord.equals2D(intPt), 'Found equal nodes with different coordinates');
103885             return ei
103886           }
103887           this._nodeMap.put(eiNew, eiNew);
103888           return eiNew
103889         };
103890         SegmentNodeList.prototype.checkSplitEdgesCorrectness = function checkSplitEdgesCorrectness (splitEdges) {
103891           var edgePts = this._edge.getCoordinates();
103892           var split0 = splitEdges.get(0);
103893           var pt0 = split0.getCoordinate(0);
103894           if (!pt0.equals2D(edgePts[0])) { throw new RuntimeException('bad split edge start point at ' + pt0) }
103895           var splitn = splitEdges.get(splitEdges.size() - 1);
103896           var splitnPts = splitn.getCoordinates();
103897           var ptn = splitnPts[splitnPts.length - 1];
103898           if (!ptn.equals2D(edgePts[edgePts.length - 1])) { throw new RuntimeException('bad split edge end point at ' + ptn) }
103899         };
103900         SegmentNodeList.prototype.interfaces_ = function interfaces_ () {
103901           return []
103902         };
103903         SegmentNodeList.prototype.getClass = function getClass () {
103904           return SegmentNodeList
103905         };
103906
103907
103908
103909         // class NodeVertexIterator {
103910         //   constructor () {
103911         //     this._nodeList = null
103912         //     this._edge = null
103913         //     this._nodeIt = null
103914         //     this._currNode = null
103915         //     this._nextNode = null
103916         //     this._currSegIndex = 0
103917         //     let nodeList = arguments[0]
103918         //     this._nodeList = nodeList
103919         //     this._edge = nodeList.getEdge()
103920         //     this._nodeIt = nodeList.iterator()
103921         //     this.readNextNode()
103922         //   }
103923         //   next () {
103924         //     if (this._currNode === null) {
103925         //       this._currNode = this._nextNode
103926         //       this._currSegIndex = this._currNode.segmentIndex
103927         //       this.readNextNode()
103928         //       return this._currNode
103929         //     }
103930         //     if (this._nextNode === null) return null
103931         //     if (this._nextNode.segmentIndex === this._currNode.segmentIndex) {
103932         //       this._currNode = this._nextNode
103933         //       this._currSegIndex = this._currNode.segmentIndex
103934         //       this.readNextNode()
103935         //       return this._currNode
103936         //     }
103937         //     if (this._nextNode.segmentIndex > this._currNode.segmentIndex) {}
103938         //     return null
103939         //   }
103940         //   remove () {
103941         //     // throw new UnsupportedOperationException(this.getClass().getName())
103942         //   }
103943         //   hasNext () {
103944         //     if (this._nextNode === null) return false
103945         //     return true
103946         //   }
103947         //   readNextNode () {
103948         //     if (this._nodeIt.hasNext()) this._nextNode = this._nodeIt.next(); else this._nextNode = null
103949         //   }
103950         //   interfaces_ () {
103951         //     return [Iterator]
103952         //   }
103953         //   getClass () {
103954         //     return NodeVertexIterator
103955         //   }
103956         // }
103957
103958         var Octant = function Octant () {};
103959
103960         Octant.prototype.interfaces_ = function interfaces_ () {
103961           return []
103962         };
103963         Octant.prototype.getClass = function getClass () {
103964           return Octant
103965         };
103966         Octant.octant = function octant () {
103967           if (typeof arguments[0] === 'number' && typeof arguments[1] === 'number') {
103968             var dx = arguments[0];
103969             var dy = arguments[1];
103970             if (dx === 0.0 && dy === 0.0) { throw new IllegalArgumentException('Cannot compute the octant for point ( ' + dx + ', ' + dy + ' )') }
103971             var adx = Math.abs(dx);
103972             var ady = Math.abs(dy);
103973             if (dx >= 0) {
103974               if (dy >= 0) {
103975                 if (adx >= ady) { return 0; } else { return 1 }
103976               } else {
103977                 if (adx >= ady) { return 7; } else { return 6 }
103978               }
103979             } else {
103980               if (dy >= 0) {
103981                 if (adx >= ady) { return 3; } else { return 2 }
103982               } else {
103983                 if (adx >= ady) { return 4; } else { return 5 }
103984               }
103985             }
103986           } else if (arguments[0] instanceof Coordinate && arguments[1] instanceof Coordinate) {
103987             var p0 = arguments[0];
103988             var p1 = arguments[1];
103989             var dx$1 = p1.x - p0.x;
103990             var dy$1 = p1.y - p0.y;
103991             if (dx$1 === 0.0 && dy$1 === 0.0) { throw new IllegalArgumentException('Cannot compute the octant for two identical points ' + p0) }
103992             return Octant.octant(dx$1, dy$1)
103993           }
103994         };
103995
103996         var SegmentString = function SegmentString () {};
103997
103998         SegmentString.prototype.getCoordinates = function getCoordinates () {};
103999         SegmentString.prototype.size = function size () {};
104000         SegmentString.prototype.getCoordinate = function getCoordinate (i) {};
104001         SegmentString.prototype.isClosed = function isClosed () {};
104002         SegmentString.prototype.setData = function setData (data) {};
104003         SegmentString.prototype.getData = function getData () {};
104004         SegmentString.prototype.interfaces_ = function interfaces_ () {
104005           return []
104006         };
104007         SegmentString.prototype.getClass = function getClass () {
104008           return SegmentString
104009         };
104010
104011         var NodableSegmentString = function NodableSegmentString () {};
104012
104013         NodableSegmentString.prototype.addIntersection = function addIntersection (intPt, segmentIndex) {};
104014         NodableSegmentString.prototype.interfaces_ = function interfaces_ () {
104015           return [SegmentString]
104016         };
104017         NodableSegmentString.prototype.getClass = function getClass () {
104018           return NodableSegmentString
104019         };
104020
104021         var NodedSegmentString = function NodedSegmentString () {
104022           this._nodeList = new SegmentNodeList(this);
104023           this._pts = null;
104024           this._data = null;
104025           var pts = arguments[0];
104026           var data = arguments[1];
104027           this._pts = pts;
104028           this._data = data;
104029         };
104030         NodedSegmentString.prototype.getCoordinates = function getCoordinates () {
104031           return this._pts
104032         };
104033         NodedSegmentString.prototype.size = function size () {
104034           return this._pts.length
104035         };
104036         NodedSegmentString.prototype.getCoordinate = function getCoordinate (i) {
104037           return this._pts[i]
104038         };
104039         NodedSegmentString.prototype.isClosed = function isClosed () {
104040           return this._pts[0].equals(this._pts[this._pts.length - 1])
104041         };
104042         NodedSegmentString.prototype.getSegmentOctant = function getSegmentOctant (index) {
104043           if (index === this._pts.length - 1) { return -1 }
104044           return this.safeOctant(this.getCoordinate(index), this.getCoordinate(index + 1))
104045         };
104046         NodedSegmentString.prototype.setData = function setData (data) {
104047           this._data = data;
104048         };
104049         NodedSegmentString.prototype.safeOctant = function safeOctant (p0, p1) {
104050           if (p0.equals2D(p1)) { return 0 }
104051           return Octant.octant(p0, p1)
104052         };
104053         NodedSegmentString.prototype.getData = function getData () {
104054           return this._data
104055         };
104056         NodedSegmentString.prototype.addIntersection = function addIntersection () {
104057           if (arguments.length === 2) {
104058             var intPt$1 = arguments[0];
104059             var segmentIndex = arguments[1];
104060             this.addIntersectionNode(intPt$1, segmentIndex);
104061           } else if (arguments.length === 4) {
104062             var li = arguments[0];
104063             var segmentIndex$1 = arguments[1];
104064             // const geomIndex = arguments[2]
104065             var intIndex = arguments[3];
104066             var intPt = new Coordinate(li.getIntersection(intIndex));
104067             this.addIntersection(intPt, segmentIndex$1);
104068           }
104069         };
104070         NodedSegmentString.prototype.toString = function toString () {
104071           return WKTWriter.toLineString(new CoordinateArraySequence(this._pts))
104072         };
104073         NodedSegmentString.prototype.getNodeList = function getNodeList () {
104074           return this._nodeList
104075         };
104076         NodedSegmentString.prototype.addIntersectionNode = function addIntersectionNode (intPt, segmentIndex) {
104077           var normalizedSegmentIndex = segmentIndex;
104078           var nextSegIndex = normalizedSegmentIndex + 1;
104079           if (nextSegIndex < this._pts.length) {
104080             var nextPt = this._pts[nextSegIndex];
104081             if (intPt.equals2D(nextPt)) {
104082               normalizedSegmentIndex = nextSegIndex;
104083             }
104084           }
104085           var ei = this._nodeList.add(intPt, normalizedSegmentIndex);
104086           return ei
104087         };
104088         NodedSegmentString.prototype.addIntersections = function addIntersections (li, segmentIndex, geomIndex) {
104089             var this$1 = this;
104090
104091           for (var i = 0; i < li.getIntersectionNum(); i++) {
104092             this$1.addIntersection(li, segmentIndex, geomIndex, i);
104093           }
104094         };
104095         NodedSegmentString.prototype.interfaces_ = function interfaces_ () {
104096           return [NodableSegmentString]
104097         };
104098         NodedSegmentString.prototype.getClass = function getClass () {
104099           return NodedSegmentString
104100         };
104101         NodedSegmentString.getNodedSubstrings = function getNodedSubstrings () {
104102           if (arguments.length === 1) {
104103             var segStrings = arguments[0];
104104             var resultEdgelist = new ArrayList();
104105             NodedSegmentString.getNodedSubstrings(segStrings, resultEdgelist);
104106             return resultEdgelist
104107           } else if (arguments.length === 2) {
104108             var segStrings$1 = arguments[0];
104109             var resultEdgelist$1 = arguments[1];
104110             for (var i = segStrings$1.iterator(); i.hasNext();) {
104111               var ss = i.next();
104112               ss.getNodeList().addSplitEdges(resultEdgelist$1);
104113             }
104114           }
104115         };
104116
104117         var LineSegment = function LineSegment () {
104118           this.p0 = null;
104119           this.p1 = null;
104120           if (arguments.length === 0) {
104121             this.p0 = new Coordinate();
104122             this.p1 = new Coordinate();
104123           } else if (arguments.length === 1) {
104124             var ls = arguments[0];
104125             this.p0 = new Coordinate(ls.p0);
104126             this.p1 = new Coordinate(ls.p1);
104127           } else if (arguments.length === 2) {
104128             this.p0 = arguments[0];
104129             this.p1 = arguments[1];
104130           } else if (arguments.length === 4) {
104131             var x0 = arguments[0];
104132             var y0 = arguments[1];
104133             var x1 = arguments[2];
104134             var y1 = arguments[3];
104135             this.p0 = new Coordinate(x0, y0);
104136             this.p1 = new Coordinate(x1, y1);
104137           }
104138         };
104139
104140         var staticAccessors$24 = { serialVersionUID: { configurable: true } };
104141         LineSegment.prototype.minX = function minX () {
104142           return Math.min(this.p0.x, this.p1.x)
104143         };
104144         LineSegment.prototype.orientationIndex = function orientationIndex () {
104145           if (arguments[0] instanceof LineSegment) {
104146             var seg = arguments[0];
104147             var orient0 = CGAlgorithms.orientationIndex(this.p0, this.p1, seg.p0);
104148             var orient1 = CGAlgorithms.orientationIndex(this.p0, this.p1, seg.p1);
104149             if (orient0 >= 0 && orient1 >= 0) { return Math.max(orient0, orient1) }
104150             if (orient0 <= 0 && orient1 <= 0) { return Math.max(orient0, orient1) }
104151             return 0
104152           } else if (arguments[0] instanceof Coordinate) {
104153             var p = arguments[0];
104154             return CGAlgorithms.orientationIndex(this.p0, this.p1, p)
104155           }
104156         };
104157         LineSegment.prototype.toGeometry = function toGeometry (geomFactory) {
104158           return geomFactory.createLineString([this.p0, this.p1])
104159         };
104160         LineSegment.prototype.isVertical = function isVertical () {
104161           return this.p0.x === this.p1.x
104162         };
104163         LineSegment.prototype.equals = function equals (o) {
104164           if (!(o instanceof LineSegment)) {
104165             return false
104166           }
104167           var other = o;
104168           return this.p0.equals(other.p0) && this.p1.equals(other.p1)
104169         };
104170         LineSegment.prototype.intersection = function intersection (line) {
104171           var li = new RobustLineIntersector();
104172           li.computeIntersection(this.p0, this.p1, line.p0, line.p1);
104173           if (li.hasIntersection()) { return li.getIntersection(0) }
104174           return null
104175         };
104176         LineSegment.prototype.project = function project () {
104177           if (arguments[0] instanceof Coordinate) {
104178             var p = arguments[0];
104179             if (p.equals(this.p0) || p.equals(this.p1)) { return new Coordinate(p) }
104180             var r = this.projectionFactor(p);
104181             var coord = new Coordinate();
104182             coord.x = this.p0.x + r * (this.p1.x - this.p0.x);
104183             coord.y = this.p0.y + r * (this.p1.y - this.p0.y);
104184             return coord
104185           } else if (arguments[0] instanceof LineSegment) {
104186             var seg = arguments[0];
104187             var pf0 = this.projectionFactor(seg.p0);
104188             var pf1 = this.projectionFactor(seg.p1);
104189             if (pf0 >= 1.0 && pf1 >= 1.0) { return null }
104190             if (pf0 <= 0.0 && pf1 <= 0.0) { return null }
104191             var newp0 = this.project(seg.p0);
104192             if (pf0 < 0.0) { newp0 = this.p0; }
104193             if (pf0 > 1.0) { newp0 = this.p1; }
104194             var newp1 = this.project(seg.p1);
104195             if (pf1 < 0.0) { newp1 = this.p0; }
104196             if (pf1 > 1.0) { newp1 = this.p1; }
104197             return new LineSegment(newp0, newp1)
104198           }
104199         };
104200         LineSegment.prototype.normalize = function normalize () {
104201           if (this.p1.compareTo(this.p0) < 0) { this.reverse(); }
104202         };
104203         LineSegment.prototype.angle = function angle () {
104204           return Math.atan2(this.p1.y - this.p0.y, this.p1.x - this.p0.x)
104205         };
104206         LineSegment.prototype.getCoordinate = function getCoordinate (i) {
104207           if (i === 0) { return this.p0 }
104208           return this.p1
104209         };
104210         LineSegment.prototype.distancePerpendicular = function distancePerpendicular (p) {
104211           return CGAlgorithms.distancePointLinePerpendicular(p, this.p0, this.p1)
104212         };
104213         LineSegment.prototype.minY = function minY () {
104214           return Math.min(this.p0.y, this.p1.y)
104215         };
104216         LineSegment.prototype.midPoint = function midPoint () {
104217           return LineSegment.midPoint(this.p0, this.p1)
104218         };
104219         LineSegment.prototype.projectionFactor = function projectionFactor (p) {
104220           if (p.equals(this.p0)) { return 0.0 }
104221           if (p.equals(this.p1)) { return 1.0 }
104222           var dx = this.p1.x - this.p0.x;
104223           var dy = this.p1.y - this.p0.y;
104224           var len = dx * dx + dy * dy;
104225           if (len <= 0.0) { return Double.NaN }
104226           var r = ((p.x - this.p0.x) * dx + (p.y - this.p0.y) * dy) / len;
104227           return r
104228         };
104229         LineSegment.prototype.closestPoints = function closestPoints (line) {
104230           var intPt = this.intersection(line);
104231           if (intPt !== null) {
104232             return [intPt, intPt]
104233           }
104234           var closestPt = new Array(2).fill(null);
104235           var minDistance = Double.MAX_VALUE;
104236           var dist = null;
104237           var close00 = this.closestPoint(line.p0);
104238           minDistance = close00.distance(line.p0);
104239           closestPt[0] = close00;
104240           closestPt[1] = line.p0;
104241           var close01 = this.closestPoint(line.p1);
104242           dist = close01.distance(line.p1);
104243           if (dist < minDistance) {
104244             minDistance = dist;
104245             closestPt[0] = close01;
104246             closestPt[1] = line.p1;
104247           }
104248           var close10 = line.closestPoint(this.p0);
104249           dist = close10.distance(this.p0);
104250           if (dist < minDistance) {
104251             minDistance = dist;
104252             closestPt[0] = this.p0;
104253             closestPt[1] = close10;
104254           }
104255           var close11 = line.closestPoint(this.p1);
104256           dist = close11.distance(this.p1);
104257           if (dist < minDistance) {
104258             minDistance = dist;
104259             closestPt[0] = this.p1;
104260             closestPt[1] = close11;
104261           }
104262           return closestPt
104263         };
104264         LineSegment.prototype.closestPoint = function closestPoint (p) {
104265           var factor = this.projectionFactor(p);
104266           if (factor > 0 && factor < 1) {
104267             return this.project(p)
104268           }
104269           var dist0 = this.p0.distance(p);
104270           var dist1 = this.p1.distance(p);
104271           if (dist0 < dist1) { return this.p0 }
104272           return this.p1
104273         };
104274         LineSegment.prototype.maxX = function maxX () {
104275           return Math.max(this.p0.x, this.p1.x)
104276         };
104277         LineSegment.prototype.getLength = function getLength () {
104278           return this.p0.distance(this.p1)
104279         };
104280         LineSegment.prototype.compareTo = function compareTo (o) {
104281           var other = o;
104282           var comp0 = this.p0.compareTo(other.p0);
104283           if (comp0 !== 0) { return comp0 }
104284           return this.p1.compareTo(other.p1)
104285         };
104286         LineSegment.prototype.reverse = function reverse () {
104287           var temp = this.p0;
104288           this.p0 = this.p1;
104289           this.p1 = temp;
104290         };
104291         LineSegment.prototype.equalsTopo = function equalsTopo (other) {
104292           return this.p0.equals(other.p0) &&
104293                 (this.p1.equals(other.p1) || this.p0.equals(other.p1)) &&
104294                  this.p1.equals(other.p0)
104295         };
104296         LineSegment.prototype.lineIntersection = function lineIntersection (line) {
104297           try {
104298             var intPt = HCoordinate.intersection(this.p0, this.p1, line.p0, line.p1);
104299             return intPt
104300           } catch (ex) {
104301             if (ex instanceof NotRepresentableException) ; else { throw ex }
104302           } finally {}
104303           return null
104304         };
104305         LineSegment.prototype.maxY = function maxY () {
104306           return Math.max(this.p0.y, this.p1.y)
104307         };
104308         LineSegment.prototype.pointAlongOffset = function pointAlongOffset (segmentLengthFraction, offsetDistance) {
104309           var segx = this.p0.x + segmentLengthFraction * (this.p1.x - this.p0.x);
104310           var segy = this.p0.y + segmentLengthFraction * (this.p1.y - this.p0.y);
104311           var dx = this.p1.x - this.p0.x;
104312           var dy = this.p1.y - this.p0.y;
104313           var len = Math.sqrt(dx * dx + dy * dy);
104314           var ux = 0.0;
104315           var uy = 0.0;
104316           if (offsetDistance !== 0.0) {
104317             if (len <= 0.0) { throw new Error('Cannot compute offset from zero-length line segment') }
104318             ux = offsetDistance * dx / len;
104319             uy = offsetDistance * dy / len;
104320           }
104321           var offsetx = segx - uy;
104322           var offsety = segy + ux;
104323           var coord = new Coordinate(offsetx, offsety);
104324           return coord
104325         };
104326         LineSegment.prototype.setCoordinates = function setCoordinates () {
104327           if (arguments.length === 1) {
104328             var ls = arguments[0];
104329             this.setCoordinates(ls.p0, ls.p1);
104330           } else if (arguments.length === 2) {
104331             var p0 = arguments[0];
104332             var p1 = arguments[1];
104333             this.p0.x = p0.x;
104334             this.p0.y = p0.y;
104335             this.p1.x = p1.x;
104336             this.p1.y = p1.y;
104337           }
104338         };
104339         LineSegment.prototype.segmentFraction = function segmentFraction (inputPt) {
104340           var segFrac = this.projectionFactor(inputPt);
104341           if (segFrac < 0.0) { segFrac = 0.0; } else if (segFrac > 1.0 || Double.isNaN(segFrac)) { segFrac = 1.0; }
104342           return segFrac
104343         };
104344         LineSegment.prototype.toString = function toString () {
104345           return 'LINESTRING( ' + this.p0.x + ' ' + this.p0.y + ', ' + this.p1.x + ' ' + this.p1.y + ')'
104346         };
104347         LineSegment.prototype.isHorizontal = function isHorizontal () {
104348           return this.p0.y === this.p1.y
104349         };
104350         LineSegment.prototype.distance = function distance () {
104351           if (arguments[0] instanceof LineSegment) {
104352             var ls = arguments[0];
104353             return CGAlgorithms.distanceLineLine(this.p0, this.p1, ls.p0, ls.p1)
104354           } else if (arguments[0] instanceof Coordinate) {
104355             var p = arguments[0];
104356             return CGAlgorithms.distancePointLine(p, this.p0, this.p1)
104357           }
104358         };
104359         LineSegment.prototype.pointAlong = function pointAlong (segmentLengthFraction) {
104360           var coord = new Coordinate();
104361           coord.x = this.p0.x + segmentLengthFraction * (this.p1.x - this.p0.x);
104362           coord.y = this.p0.y + segmentLengthFraction * (this.p1.y - this.p0.y);
104363           return coord
104364         };
104365         LineSegment.prototype.hashCode = function hashCode () {
104366           var bits0 = Double.doubleToLongBits(this.p0.x);
104367           bits0 ^= Double.doubleToLongBits(this.p0.y) * 31;
104368           var hash0 = Math.trunc(bits0) ^ Math.trunc(bits0 >> 32);
104369           var bits1 = Double.doubleToLongBits(this.p1.x);
104370           bits1 ^= Double.doubleToLongBits(this.p1.y) * 31;
104371           var hash1 = Math.trunc(bits1) ^ Math.trunc(bits1 >> 32);
104372           return hash0 ^ hash1
104373         };
104374         LineSegment.prototype.interfaces_ = function interfaces_ () {
104375           return [Comparable, Serializable]
104376         };
104377         LineSegment.prototype.getClass = function getClass () {
104378           return LineSegment
104379         };
104380         LineSegment.midPoint = function midPoint (p0, p1) {
104381           return new Coordinate((p0.x + p1.x) / 2, (p0.y + p1.y) / 2)
104382         };
104383         staticAccessors$24.serialVersionUID.get = function () { return 3252005833466256227 };
104384
104385         Object.defineProperties( LineSegment, staticAccessors$24 );
104386
104387         var MonotoneChainOverlapAction = function MonotoneChainOverlapAction () {
104388           this.tempEnv1 = new Envelope();
104389           this.tempEnv2 = new Envelope();
104390           this._overlapSeg1 = new LineSegment();
104391           this._overlapSeg2 = new LineSegment();
104392         };
104393         MonotoneChainOverlapAction.prototype.overlap = function overlap () {
104394           if (arguments.length === 2) ; else if (arguments.length === 4) {
104395             var mc1 = arguments[0];
104396             var start1 = arguments[1];
104397             var mc2 = arguments[2];
104398             var start2 = arguments[3];
104399             mc1.getLineSegment(start1, this._overlapSeg1);
104400             mc2.getLineSegment(start2, this._overlapSeg2);
104401             this.overlap(this._overlapSeg1, this._overlapSeg2);
104402           }
104403         };
104404         MonotoneChainOverlapAction.prototype.interfaces_ = function interfaces_ () {
104405           return []
104406         };
104407         MonotoneChainOverlapAction.prototype.getClass = function getClass () {
104408           return MonotoneChainOverlapAction
104409         };
104410
104411         var MonotoneChain = function MonotoneChain () {
104412           this._pts = null;
104413           this._start = null;
104414           this._end = null;
104415           this._env = null;
104416           this._context = null;
104417           this._id = null;
104418           var pts = arguments[0];
104419           var start = arguments[1];
104420           var end = arguments[2];
104421           var context = arguments[3];
104422           this._pts = pts;
104423           this._start = start;
104424           this._end = end;
104425           this._context = context;
104426         };
104427         MonotoneChain.prototype.getLineSegment = function getLineSegment (index, ls) {
104428           ls.p0 = this._pts[index];
104429           ls.p1 = this._pts[index + 1];
104430         };
104431         MonotoneChain.prototype.computeSelect = function computeSelect (searchEnv, start0, end0, mcs) {
104432           var p0 = this._pts[start0];
104433           var p1 = this._pts[end0];
104434           mcs.tempEnv1.init(p0, p1);
104435           if (end0 - start0 === 1) {
104436             mcs.select(this, start0);
104437             return null
104438           }
104439           if (!searchEnv.intersects(mcs.tempEnv1)) { return null }
104440           var mid = Math.trunc((start0 + end0) / 2);
104441           if (start0 < mid) {
104442             this.computeSelect(searchEnv, start0, mid, mcs);
104443           }
104444           if (mid < end0) {
104445             this.computeSelect(searchEnv, mid, end0, mcs);
104446           }
104447         };
104448         MonotoneChain.prototype.getCoordinates = function getCoordinates () {
104449             var this$1 = this;
104450
104451           var coord = new Array(this._end - this._start + 1).fill(null);
104452           var index = 0;
104453           for (var i = this._start; i <= this._end; i++) {
104454             coord[index++] = this$1._pts[i];
104455           }
104456           return coord
104457         };
104458         MonotoneChain.prototype.computeOverlaps = function computeOverlaps (mc, mco) {
104459           this.computeOverlapsInternal(this._start, this._end, mc, mc._start, mc._end, mco);
104460         };
104461         MonotoneChain.prototype.setId = function setId (id) {
104462           this._id = id;
104463         };
104464         MonotoneChain.prototype.select = function select (searchEnv, mcs) {
104465           this.computeSelect(searchEnv, this._start, this._end, mcs);
104466         };
104467         MonotoneChain.prototype.getEnvelope = function getEnvelope () {
104468           if (this._env === null) {
104469             var p0 = this._pts[this._start];
104470             var p1 = this._pts[this._end];
104471             this._env = new Envelope(p0, p1);
104472           }
104473           return this._env
104474         };
104475         MonotoneChain.prototype.getEndIndex = function getEndIndex () {
104476           return this._end
104477         };
104478         MonotoneChain.prototype.getStartIndex = function getStartIndex () {
104479           return this._start
104480         };
104481         MonotoneChain.prototype.getContext = function getContext () {
104482           return this._context
104483         };
104484         MonotoneChain.prototype.getId = function getId () {
104485           return this._id
104486         };
104487         MonotoneChain.prototype.computeOverlapsInternal = function computeOverlapsInternal (start0, end0, mc, start1, end1, mco) {
104488           var p00 = this._pts[start0];
104489           var p01 = this._pts[end0];
104490           var p10 = mc._pts[start1];
104491           var p11 = mc._pts[end1];
104492           if (end0 - start0 === 1 && end1 - start1 === 1) {
104493             mco.overlap(this, start0, mc, start1);
104494             return null
104495           }
104496           mco.tempEnv1.init(p00, p01);
104497           mco.tempEnv2.init(p10, p11);
104498           if (!mco.tempEnv1.intersects(mco.tempEnv2)) { return null }
104499           var mid0 = Math.trunc((start0 + end0) / 2);
104500           var mid1 = Math.trunc((start1 + end1) / 2);
104501           if (start0 < mid0) {
104502             if (start1 < mid1) { this.computeOverlapsInternal(start0, mid0, mc, start1, mid1, mco); }
104503             if (mid1 < end1) { this.computeOverlapsInternal(start0, mid0, mc, mid1, end1, mco); }
104504           }
104505           if (mid0 < end0) {
104506             if (start1 < mid1) { this.computeOverlapsInternal(mid0, end0, mc, start1, mid1, mco); }
104507             if (mid1 < end1) { this.computeOverlapsInternal(mid0, end0, mc, mid1, end1, mco); }
104508           }
104509         };
104510         MonotoneChain.prototype.interfaces_ = function interfaces_ () {
104511           return []
104512         };
104513         MonotoneChain.prototype.getClass = function getClass () {
104514           return MonotoneChain
104515         };
104516
104517         var MonotoneChainBuilder = function MonotoneChainBuilder () {};
104518
104519         MonotoneChainBuilder.prototype.interfaces_ = function interfaces_ () {
104520           return []
104521         };
104522         MonotoneChainBuilder.prototype.getClass = function getClass () {
104523           return MonotoneChainBuilder
104524         };
104525         MonotoneChainBuilder.getChainStartIndices = function getChainStartIndices (pts) {
104526           var start = 0;
104527           var startIndexList = new ArrayList();
104528           startIndexList.add(new Integer(start));
104529           do {
104530             var last = MonotoneChainBuilder.findChainEnd(pts, start);
104531             startIndexList.add(new Integer(last));
104532             start = last;
104533           } while (start < pts.length - 1)
104534           var startIndex = MonotoneChainBuilder.toIntArray(startIndexList);
104535           return startIndex
104536         };
104537         MonotoneChainBuilder.findChainEnd = function findChainEnd (pts, start) {
104538           var safeStart = start;
104539           while (safeStart < pts.length - 1 && pts[safeStart].equals2D(pts[safeStart + 1])) {
104540             safeStart++;
104541           }
104542           if (safeStart >= pts.length - 1) {
104543             return pts.length - 1
104544           }
104545           var chainQuad = Quadrant.quadrant(pts[safeStart], pts[safeStart + 1]);
104546           var last = start + 1;
104547           while (last < pts.length) {
104548             if (!pts[last - 1].equals2D(pts[last])) {
104549               var quad = Quadrant.quadrant(pts[last - 1], pts[last]);
104550               if (quad !== chainQuad) { break }
104551             }
104552             last++;
104553           }
104554           return last - 1
104555         };
104556         MonotoneChainBuilder.getChains = function getChains () {
104557           if (arguments.length === 1) {
104558             var pts = arguments[0];
104559             return MonotoneChainBuilder.getChains(pts, null)
104560           } else if (arguments.length === 2) {
104561             var pts$1 = arguments[0];
104562             var context = arguments[1];
104563             var mcList = new ArrayList();
104564             var startIndex = MonotoneChainBuilder.getChainStartIndices(pts$1);
104565             for (var i = 0; i < startIndex.length - 1; i++) {
104566               var mc = new MonotoneChain(pts$1, startIndex[i], startIndex[i + 1], context);
104567               mcList.add(mc);
104568             }
104569             return mcList
104570           }
104571         };
104572         MonotoneChainBuilder.toIntArray = function toIntArray (list) {
104573           var array = new Array(list.size()).fill(null);
104574           for (var i = 0; i < array.length; i++) {
104575             array[i] = list.get(i).intValue();
104576           }
104577           return array
104578         };
104579
104580         var Noder = function Noder () {};
104581
104582         Noder.prototype.computeNodes = function computeNodes (segStrings) {};
104583         Noder.prototype.getNodedSubstrings = function getNodedSubstrings () {};
104584         Noder.prototype.interfaces_ = function interfaces_ () {
104585           return []
104586         };
104587         Noder.prototype.getClass = function getClass () {
104588           return Noder
104589         };
104590
104591         var SinglePassNoder = function SinglePassNoder () {
104592           this._segInt = null;
104593           if (arguments.length === 0) ; else if (arguments.length === 1) {
104594             var segInt = arguments[0];
104595             this.setSegmentIntersector(segInt);
104596           }
104597         };
104598         SinglePassNoder.prototype.setSegmentIntersector = function setSegmentIntersector (segInt) {
104599           this._segInt = segInt;
104600         };
104601         SinglePassNoder.prototype.interfaces_ = function interfaces_ () {
104602           return [Noder]
104603         };
104604         SinglePassNoder.prototype.getClass = function getClass () {
104605           return SinglePassNoder
104606         };
104607
104608         var MCIndexNoder = (function (SinglePassNoder$$1) {
104609           function MCIndexNoder (si) {
104610             if (si) { SinglePassNoder$$1.call(this, si); }
104611             else { SinglePassNoder$$1.call(this); }
104612             this._monoChains = new ArrayList();
104613             this._index = new STRtree();
104614             this._idCounter = 0;
104615             this._nodedSegStrings = null;
104616             this._nOverlaps = 0;
104617           }
104618
104619           if ( SinglePassNoder$$1 ) { MCIndexNoder.__proto__ = SinglePassNoder$$1; }
104620           MCIndexNoder.prototype = Object.create( SinglePassNoder$$1 && SinglePassNoder$$1.prototype );
104621           MCIndexNoder.prototype.constructor = MCIndexNoder;
104622
104623           var staticAccessors = { SegmentOverlapAction: { configurable: true } };
104624           MCIndexNoder.prototype.getMonotoneChains = function getMonotoneChains () {
104625             return this._monoChains
104626           };
104627           MCIndexNoder.prototype.getNodedSubstrings = function getNodedSubstrings () {
104628             return NodedSegmentString.getNodedSubstrings(this._nodedSegStrings)
104629           };
104630           MCIndexNoder.prototype.getIndex = function getIndex () {
104631             return this._index
104632           };
104633           MCIndexNoder.prototype.add = function add (segStr) {
104634             var this$1 = this;
104635
104636             var segChains = MonotoneChainBuilder.getChains(segStr.getCoordinates(), segStr);
104637             for (var i = segChains.iterator(); i.hasNext();) {
104638               var mc = i.next();
104639               mc.setId(this$1._idCounter++);
104640               this$1._index.insert(mc.getEnvelope(), mc);
104641               this$1._monoChains.add(mc);
104642             }
104643           };
104644           MCIndexNoder.prototype.computeNodes = function computeNodes (inputSegStrings) {
104645             var this$1 = this;
104646
104647             this._nodedSegStrings = inputSegStrings;
104648             for (var i = inputSegStrings.iterator(); i.hasNext();) {
104649               this$1.add(i.next());
104650             }
104651             this.intersectChains();
104652           };
104653           MCIndexNoder.prototype.intersectChains = function intersectChains () {
104654             var this$1 = this;
104655
104656             var overlapAction = new SegmentOverlapAction(this._segInt);
104657             for (var i = this._monoChains.iterator(); i.hasNext();) {
104658               var queryChain = i.next();
104659               var overlapChains = this$1._index.query(queryChain.getEnvelope());
104660               for (var j = overlapChains.iterator(); j.hasNext();) {
104661                 var testChain = j.next();
104662                 if (testChain.getId() > queryChain.getId()) {
104663                   queryChain.computeOverlaps(testChain, overlapAction);
104664                   this$1._nOverlaps++;
104665                 }
104666                 if (this$1._segInt.isDone()) { return null }
104667               }
104668             }
104669           };
104670           MCIndexNoder.prototype.interfaces_ = function interfaces_ () {
104671             return []
104672           };
104673           MCIndexNoder.prototype.getClass = function getClass () {
104674             return MCIndexNoder
104675           };
104676           staticAccessors.SegmentOverlapAction.get = function () { return SegmentOverlapAction };
104677
104678           Object.defineProperties( MCIndexNoder, staticAccessors );
104679
104680           return MCIndexNoder;
104681         }(SinglePassNoder));
104682
104683         var SegmentOverlapAction = (function (MonotoneChainOverlapAction$$1) {
104684           function SegmentOverlapAction () {
104685             MonotoneChainOverlapAction$$1.call(this);
104686             this._si = null;
104687             var si = arguments[0];
104688             this._si = si;
104689           }
104690
104691           if ( MonotoneChainOverlapAction$$1 ) { SegmentOverlapAction.__proto__ = MonotoneChainOverlapAction$$1; }
104692           SegmentOverlapAction.prototype = Object.create( MonotoneChainOverlapAction$$1 && MonotoneChainOverlapAction$$1.prototype );
104693           SegmentOverlapAction.prototype.constructor = SegmentOverlapAction;
104694           SegmentOverlapAction.prototype.overlap = function overlap () {
104695             if (arguments.length === 4) {
104696               var mc1 = arguments[0];
104697               var start1 = arguments[1];
104698               var mc2 = arguments[2];
104699               var start2 = arguments[3];
104700               var ss1 = mc1.getContext();
104701               var ss2 = mc2.getContext();
104702               this._si.processIntersections(ss1, start1, ss2, start2);
104703             } else { return MonotoneChainOverlapAction$$1.prototype.overlap.apply(this, arguments) }
104704           };
104705           SegmentOverlapAction.prototype.interfaces_ = function interfaces_ () {
104706             return []
104707           };
104708           SegmentOverlapAction.prototype.getClass = function getClass () {
104709             return SegmentOverlapAction
104710           };
104711
104712           return SegmentOverlapAction;
104713         }(MonotoneChainOverlapAction));
104714
104715         var BufferParameters = function BufferParameters () {
104716           this._quadrantSegments = BufferParameters.DEFAULT_QUADRANT_SEGMENTS;
104717           this._endCapStyle = BufferParameters.CAP_ROUND;
104718           this._joinStyle = BufferParameters.JOIN_ROUND;
104719           this._mitreLimit = BufferParameters.DEFAULT_MITRE_LIMIT;
104720           this._isSingleSided = false;
104721           this._simplifyFactor = BufferParameters.DEFAULT_SIMPLIFY_FACTOR;
104722
104723           if (arguments.length === 0) ; else if (arguments.length === 1) {
104724             var quadrantSegments = arguments[0];
104725             this.setQuadrantSegments(quadrantSegments);
104726           } else if (arguments.length === 2) {
104727             var quadrantSegments$1 = arguments[0];
104728             var endCapStyle = arguments[1];
104729             this.setQuadrantSegments(quadrantSegments$1);
104730             this.setEndCapStyle(endCapStyle);
104731           } else if (arguments.length === 4) {
104732             var quadrantSegments$2 = arguments[0];
104733             var endCapStyle$1 = arguments[1];
104734             var joinStyle = arguments[2];
104735             var mitreLimit = arguments[3];
104736             this.setQuadrantSegments(quadrantSegments$2);
104737             this.setEndCapStyle(endCapStyle$1);
104738             this.setJoinStyle(joinStyle);
104739             this.setMitreLimit(mitreLimit);
104740           }
104741         };
104742
104743         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 } };
104744         BufferParameters.prototype.getEndCapStyle = function getEndCapStyle () {
104745           return this._endCapStyle
104746         };
104747         BufferParameters.prototype.isSingleSided = function isSingleSided () {
104748           return this._isSingleSided
104749         };
104750         BufferParameters.prototype.setQuadrantSegments = function setQuadrantSegments (quadSegs) {
104751           this._quadrantSegments = quadSegs;
104752           if (this._quadrantSegments === 0) { this._joinStyle = BufferParameters.JOIN_BEVEL; }
104753           if (this._quadrantSegments < 0) {
104754             this._joinStyle = BufferParameters.JOIN_MITRE;
104755             this._mitreLimit = Math.abs(this._quadrantSegments);
104756           }
104757           if (quadSegs <= 0) {
104758             this._quadrantSegments = 1;
104759           }
104760           if (this._joinStyle !== BufferParameters.JOIN_ROUND) {
104761             this._quadrantSegments = BufferParameters.DEFAULT_QUADRANT_SEGMENTS;
104762           }
104763         };
104764         BufferParameters.prototype.getJoinStyle = function getJoinStyle () {
104765           return this._joinStyle
104766         };
104767         BufferParameters.prototype.setJoinStyle = function setJoinStyle (joinStyle) {
104768           this._joinStyle = joinStyle;
104769         };
104770         BufferParameters.prototype.setSimplifyFactor = function setSimplifyFactor (simplifyFactor) {
104771           this._simplifyFactor = simplifyFactor < 0 ? 0 : simplifyFactor;
104772         };
104773         BufferParameters.prototype.getSimplifyFactor = function getSimplifyFactor () {
104774           return this._simplifyFactor
104775         };
104776         BufferParameters.prototype.getQuadrantSegments = function getQuadrantSegments () {
104777           return this._quadrantSegments
104778         };
104779         BufferParameters.prototype.setEndCapStyle = function setEndCapStyle (endCapStyle) {
104780           this._endCapStyle = endCapStyle;
104781         };
104782         BufferParameters.prototype.getMitreLimit = function getMitreLimit () {
104783           return this._mitreLimit
104784         };
104785         BufferParameters.prototype.setMitreLimit = function setMitreLimit (mitreLimit) {
104786           this._mitreLimit = mitreLimit;
104787         };
104788         BufferParameters.prototype.setSingleSided = function setSingleSided (isSingleSided) {
104789           this._isSingleSided = isSingleSided;
104790         };
104791         BufferParameters.prototype.interfaces_ = function interfaces_ () {
104792           return []
104793         };
104794         BufferParameters.prototype.getClass = function getClass () {
104795           return BufferParameters
104796         };
104797         BufferParameters.bufferDistanceError = function bufferDistanceError (quadSegs) {
104798           var alpha = Math.PI / 2.0 / quadSegs;
104799           return 1 - Math.cos(alpha / 2.0)
104800         };
104801         staticAccessors$25.CAP_ROUND.get = function () { return 1 };
104802         staticAccessors$25.CAP_FLAT.get = function () { return 2 };
104803         staticAccessors$25.CAP_SQUARE.get = function () { return 3 };
104804         staticAccessors$25.JOIN_ROUND.get = function () { return 1 };
104805         staticAccessors$25.JOIN_MITRE.get = function () { return 2 };
104806         staticAccessors$25.JOIN_BEVEL.get = function () { return 3 };
104807         staticAccessors$25.DEFAULT_QUADRANT_SEGMENTS.get = function () { return 8 };
104808         staticAccessors$25.DEFAULT_MITRE_LIMIT.get = function () { return 5.0 };
104809         staticAccessors$25.DEFAULT_SIMPLIFY_FACTOR.get = function () { return 0.01 };
104810
104811         Object.defineProperties( BufferParameters, staticAccessors$25 );
104812
104813         var BufferInputLineSimplifier = function BufferInputLineSimplifier (inputLine) {
104814           this._distanceTol = null;
104815           this._isDeleted = null;
104816           this._angleOrientation = CGAlgorithms.COUNTERCLOCKWISE;
104817           this._inputLine = inputLine || null;
104818         };
104819
104820         var staticAccessors$26 = { INIT: { configurable: true },DELETE: { configurable: true },KEEP: { configurable: true },NUM_PTS_TO_CHECK: { configurable: true } };
104821         BufferInputLineSimplifier.prototype.isDeletable = function isDeletable (i0, i1, i2, distanceTol) {
104822           var p0 = this._inputLine[i0];
104823           var p1 = this._inputLine[i1];
104824           var p2 = this._inputLine[i2];
104825           if (!this.isConcave(p0, p1, p2)) { return false }
104826           if (!this.isShallow(p0, p1, p2, distanceTol)) { return false }
104827           return this.isShallowSampled(p0, p1, i0, i2, distanceTol)
104828         };
104829         BufferInputLineSimplifier.prototype.deleteShallowConcavities = function deleteShallowConcavities () {
104830             var this$1 = this;
104831
104832           var index = 1;
104833           // const maxIndex = this._inputLine.length - 1
104834           var midIndex = this.findNextNonDeletedIndex(index);
104835           var lastIndex = this.findNextNonDeletedIndex(midIndex);
104836           var isChanged = false;
104837           while (lastIndex < this._inputLine.length) {
104838             var isMiddleVertexDeleted = false;
104839             if (this$1.isDeletable(index, midIndex, lastIndex, this$1._distanceTol)) {
104840               this$1._isDeleted[midIndex] = BufferInputLineSimplifier.DELETE;
104841               isMiddleVertexDeleted = true;
104842               isChanged = true;
104843             }
104844             if (isMiddleVertexDeleted) { index = lastIndex; } else { index = midIndex; }
104845             midIndex = this$1.findNextNonDeletedIndex(index);
104846             lastIndex = this$1.findNextNonDeletedIndex(midIndex);
104847           }
104848           return isChanged
104849         };
104850         BufferInputLineSimplifier.prototype.isShallowConcavity = function isShallowConcavity (p0, p1, p2, distanceTol) {
104851           var orientation = CGAlgorithms.computeOrientation(p0, p1, p2);
104852           var isAngleToSimplify = orientation === this._angleOrientation;
104853           if (!isAngleToSimplify) { return false }
104854           var dist = CGAlgorithms.distancePointLine(p1, p0, p2);
104855           return dist < distanceTol
104856         };
104857         BufferInputLineSimplifier.prototype.isShallowSampled = function isShallowSampled (p0, p2, i0, i2, distanceTol) {
104858             var this$1 = this;
104859
104860           var inc = Math.trunc((i2 - i0) / BufferInputLineSimplifier.NUM_PTS_TO_CHECK);
104861           if (inc <= 0) { inc = 1; }
104862           for (var i = i0; i < i2; i += inc) {
104863             if (!this$1.isShallow(p0, p2, this$1._inputLine[i], distanceTol)) { return false }
104864           }
104865           return true
104866         };
104867         BufferInputLineSimplifier.prototype.isConcave = function isConcave (p0, p1, p2) {
104868           var orientation = CGAlgorithms.computeOrientation(p0, p1, p2);
104869           var isConcave = orientation === this._angleOrientation;
104870           return isConcave
104871         };
104872         BufferInputLineSimplifier.prototype.simplify = function simplify (distanceTol) {
104873             var this$1 = this;
104874
104875           this._distanceTol = Math.abs(distanceTol);
104876           if (distanceTol < 0) { this._angleOrientation = CGAlgorithms.CLOCKWISE; }
104877           this._isDeleted = new Array(this._inputLine.length).fill(null);
104878           var isChanged = false;
104879           do {
104880             isChanged = this$1.deleteShallowConcavities();
104881           } while (isChanged)
104882           return this.collapseLine()
104883         };
104884         BufferInputLineSimplifier.prototype.findNextNonDeletedIndex = function findNextNonDeletedIndex (index) {
104885           var next = index + 1;
104886           while (next < this._inputLine.length && this._isDeleted[next] === BufferInputLineSimplifier.DELETE) { next++; }
104887           return next
104888         };
104889         BufferInputLineSimplifier.prototype.isShallow = function isShallow (p0, p1, p2, distanceTol) {
104890           var dist = CGAlgorithms.distancePointLine(p1, p0, p2);
104891           return dist < distanceTol
104892         };
104893         BufferInputLineSimplifier.prototype.collapseLine = function collapseLine () {
104894             var this$1 = this;
104895
104896           var coordList = new CoordinateList();
104897           for (var i = 0; i < this._inputLine.length; i++) {
104898             if (this$1._isDeleted[i] !== BufferInputLineSimplifier.DELETE) { coordList.add(this$1._inputLine[i]); }
104899           }
104900           return coordList.toCoordinateArray()
104901         };
104902         BufferInputLineSimplifier.prototype.interfaces_ = function interfaces_ () {
104903           return []
104904         };
104905         BufferInputLineSimplifier.prototype.getClass = function getClass () {
104906           return BufferInputLineSimplifier
104907         };
104908         BufferInputLineSimplifier.simplify = function simplify (inputLine, distanceTol) {
104909           var simp = new BufferInputLineSimplifier(inputLine);
104910           return simp.simplify(distanceTol)
104911         };
104912         staticAccessors$26.INIT.get = function () { return 0 };
104913         staticAccessors$26.DELETE.get = function () { return 1 };
104914         staticAccessors$26.KEEP.get = function () { return 1 };
104915         staticAccessors$26.NUM_PTS_TO_CHECK.get = function () { return 10 };
104916
104917         Object.defineProperties( BufferInputLineSimplifier, staticAccessors$26 );
104918
104919         var OffsetSegmentString = function OffsetSegmentString () {
104920           this._ptList = null;
104921           this._precisionModel = null;
104922           this._minimimVertexDistance = 0.0;
104923           this._ptList = new ArrayList();
104924         };
104925
104926         var staticAccessors$28 = { COORDINATE_ARRAY_TYPE: { configurable: true } };
104927         OffsetSegmentString.prototype.getCoordinates = function getCoordinates () {
104928           var coord = this._ptList.toArray(OffsetSegmentString.COORDINATE_ARRAY_TYPE);
104929           return coord
104930         };
104931         OffsetSegmentString.prototype.setPrecisionModel = function setPrecisionModel (precisionModel) {
104932           this._precisionModel = precisionModel;
104933         };
104934         OffsetSegmentString.prototype.addPt = function addPt (pt) {
104935           var bufPt = new Coordinate(pt);
104936           this._precisionModel.makePrecise(bufPt);
104937           if (this.isRedundant(bufPt)) { return null }
104938           this._ptList.add(bufPt);
104939         };
104940         OffsetSegmentString.prototype.revere = function revere () {};
104941         OffsetSegmentString.prototype.addPts = function addPts (pt, isForward) {
104942             var this$1 = this;
104943
104944           if (isForward) {
104945             for (var i = 0; i < pt.length; i++) {
104946               this$1.addPt(pt[i]);
104947             }
104948           } else {
104949             for (var i$1 = pt.length - 1; i$1 >= 0; i$1--) {
104950               this$1.addPt(pt[i$1]);
104951             }
104952           }
104953         };
104954         OffsetSegmentString.prototype.isRedundant = function isRedundant (pt) {
104955           if (this._ptList.size() < 1) { return false }
104956           var lastPt = this._ptList.get(this._ptList.size() - 1);
104957           var ptDist = pt.distance(lastPt);
104958           if (ptDist < this._minimimVertexDistance) { return true }
104959           return false
104960         };
104961         OffsetSegmentString.prototype.toString = function toString () {
104962           var fact = new GeometryFactory();
104963           var line = fact.createLineString(this.getCoordinates());
104964           return line.toString()
104965         };
104966         OffsetSegmentString.prototype.closeRing = function closeRing () {
104967           if (this._ptList.size() < 1) { return null }
104968           var startPt = new Coordinate(this._ptList.get(0));
104969           var lastPt = this._ptList.get(this._ptList.size() - 1);
104970           // const last2Pt = null
104971           // if (this._ptList.size() >= 2) last2Pt = this._ptList.get(this._ptList.size() - 2)
104972           if (startPt.equals(lastPt)) { return null }
104973           this._ptList.add(startPt);
104974         };
104975         OffsetSegmentString.prototype.setMinimumVertexDistance = function setMinimumVertexDistance (minimimVertexDistance) {
104976           this._minimimVertexDistance = minimimVertexDistance;
104977         };
104978         OffsetSegmentString.prototype.interfaces_ = function interfaces_ () {
104979           return []
104980         };
104981         OffsetSegmentString.prototype.getClass = function getClass () {
104982           return OffsetSegmentString
104983         };
104984         staticAccessors$28.COORDINATE_ARRAY_TYPE.get = function () { return new Array(0).fill(null) };
104985
104986         Object.defineProperties( OffsetSegmentString, staticAccessors$28 );
104987
104988         var Angle = function Angle () {};
104989
104990         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 } };
104991
104992         Angle.prototype.interfaces_ = function interfaces_ () {
104993           return []
104994         };
104995         Angle.prototype.getClass = function getClass () {
104996           return Angle
104997         };
104998         Angle.toDegrees = function toDegrees (radians) {
104999           return radians * 180 / Math.PI
105000         };
105001         Angle.normalize = function normalize (angle) {
105002           while (angle > Math.PI) { angle -= Angle.PI_TIMES_2; }
105003           while (angle <= -Math.PI) { angle += Angle.PI_TIMES_2; }
105004           return angle
105005         };
105006         Angle.angle = function angle () {
105007           if (arguments.length === 1) {
105008             var p = arguments[0];
105009             return Math.atan2(p.y, p.x)
105010           } else if (arguments.length === 2) {
105011             var p0 = arguments[0];
105012             var p1 = arguments[1];
105013             var dx = p1.x - p0.x;
105014             var dy = p1.y - p0.y;
105015             return Math.atan2(dy, dx)
105016           }
105017         };
105018         Angle.isAcute = function isAcute (p0, p1, p2) {
105019           var dx0 = p0.x - p1.x;
105020           var dy0 = p0.y - p1.y;
105021           var dx1 = p2.x - p1.x;
105022           var dy1 = p2.y - p1.y;
105023           var dotprod = dx0 * dx1 + dy0 * dy1;
105024           return dotprod > 0
105025         };
105026         Angle.isObtuse = function isObtuse (p0, p1, p2) {
105027           var dx0 = p0.x - p1.x;
105028           var dy0 = p0.y - p1.y;
105029           var dx1 = p2.x - p1.x;
105030           var dy1 = p2.y - p1.y;
105031           var dotprod = dx0 * dx1 + dy0 * dy1;
105032           return dotprod < 0
105033         };
105034         Angle.interiorAngle = function interiorAngle (p0, p1, p2) {
105035           var anglePrev = Angle.angle(p1, p0);
105036           var angleNext = Angle.angle(p1, p2);
105037           return Math.abs(angleNext - anglePrev)
105038         };
105039         Angle.normalizePositive = function normalizePositive (angle) {
105040           if (angle < 0.0) {
105041             while (angle < 0.0) { angle += Angle.PI_TIMES_2; }
105042             if (angle >= Angle.PI_TIMES_2) { angle = 0.0; }
105043           } else {
105044             while (angle >= Angle.PI_TIMES_2) { angle -= Angle.PI_TIMES_2; }
105045             if (angle < 0.0) { angle = 0.0; }
105046           }
105047           return angle
105048         };
105049         Angle.angleBetween = function angleBetween (tip1, tail, tip2) {
105050           var a1 = Angle.angle(tail, tip1);
105051           var a2 = Angle.angle(tail, tip2);
105052           return Angle.diff(a1, a2)
105053         };
105054         Angle.diff = function diff (ang1, ang2) {
105055           var delAngle = null;
105056           if (ang1 < ang2) {
105057             delAngle = ang2 - ang1;
105058           } else {
105059             delAngle = ang1 - ang2;
105060           }
105061           if (delAngle > Math.PI) {
105062             delAngle = 2 * Math.PI - delAngle;
105063           }
105064           return delAngle
105065         };
105066         Angle.toRadians = function toRadians (angleDegrees) {
105067           return angleDegrees * Math.PI / 180.0
105068         };
105069         Angle.getTurn = function getTurn (ang1, ang2) {
105070           var crossproduct = Math.sin(ang2 - ang1);
105071           if (crossproduct > 0) {
105072             return Angle.COUNTERCLOCKWISE
105073           }
105074           if (crossproduct < 0) {
105075             return Angle.CLOCKWISE
105076           }
105077           return Angle.NONE
105078         };
105079         Angle.angleBetweenOriented = function angleBetweenOriented (tip1, tail, tip2) {
105080           var a1 = Angle.angle(tail, tip1);
105081           var a2 = Angle.angle(tail, tip2);
105082           var angDel = a2 - a1;
105083           if (angDel <= -Math.PI) { return angDel + Angle.PI_TIMES_2 }
105084           if (angDel > Math.PI) { return angDel - Angle.PI_TIMES_2 }
105085           return angDel
105086         };
105087         staticAccessors$29.PI_TIMES_2.get = function () { return 2.0 * Math.PI };
105088         staticAccessors$29.PI_OVER_2.get = function () { return Math.PI / 2.0 };
105089         staticAccessors$29.PI_OVER_4.get = function () { return Math.PI / 4.0 };
105090         staticAccessors$29.COUNTERCLOCKWISE.get = function () { return CGAlgorithms.COUNTERCLOCKWISE };
105091         staticAccessors$29.CLOCKWISE.get = function () { return CGAlgorithms.CLOCKWISE };
105092         staticAccessors$29.NONE.get = function () { return CGAlgorithms.COLLINEAR };
105093
105094         Object.defineProperties( Angle, staticAccessors$29 );
105095
105096         var OffsetSegmentGenerator = function OffsetSegmentGenerator () {
105097           this._maxCurveSegmentError = 0.0;
105098           this._filletAngleQuantum = null;
105099           this._closingSegLengthFactor = 1;
105100           this._segList = null;
105101           this._distance = 0.0;
105102           this._precisionModel = null;
105103           this._bufParams = null;
105104           this._li = null;
105105           this._s0 = null;
105106           this._s1 = null;
105107           this._s2 = null;
105108           this._seg0 = new LineSegment();
105109           this._seg1 = new LineSegment();
105110           this._offset0 = new LineSegment();
105111           this._offset1 = new LineSegment();
105112           this._side = 0;
105113           this._hasNarrowConcaveAngle = false;
105114           var precisionModel = arguments[0];
105115           var bufParams = arguments[1];
105116           var distance = arguments[2];
105117           this._precisionModel = precisionModel;
105118           this._bufParams = bufParams;
105119           this._li = new RobustLineIntersector();
105120           this._filletAngleQuantum = Math.PI / 2.0 / bufParams.getQuadrantSegments();
105121           if (bufParams.getQuadrantSegments() >= 8 && bufParams.getJoinStyle() === BufferParameters.JOIN_ROUND) { this._closingSegLengthFactor = OffsetSegmentGenerator.MAX_CLOSING_SEG_LEN_FACTOR; }
105122           this.init(distance);
105123         };
105124
105125         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 } };
105126         OffsetSegmentGenerator.prototype.addNextSegment = function addNextSegment (p, addStartPoint) {
105127           this._s0 = this._s1;
105128           this._s1 = this._s2;
105129           this._s2 = p;
105130           this._seg0.setCoordinates(this._s0, this._s1);
105131           this.computeOffsetSegment(this._seg0, this._side, this._distance, this._offset0);
105132           this._seg1.setCoordinates(this._s1, this._s2);
105133           this.computeOffsetSegment(this._seg1, this._side, this._distance, this._offset1);
105134           if (this._s1.equals(this._s2)) { return null }
105135           var orientation = CGAlgorithms.computeOrientation(this._s0, this._s1, this._s2);
105136           var outsideTurn = (orientation === CGAlgorithms.CLOCKWISE && this._side === Position.LEFT) || (orientation === CGAlgorithms.COUNTERCLOCKWISE && this._side === Position.RIGHT);
105137           if (orientation === 0) {
105138             this.addCollinear(addStartPoint);
105139           } else if (outsideTurn) {
105140             this.addOutsideTurn(orientation, addStartPoint);
105141           } else {
105142             this.addInsideTurn(orientation, addStartPoint);
105143           }
105144         };
105145         OffsetSegmentGenerator.prototype.addLineEndCap = function addLineEndCap (p0, p1) {
105146           var seg = new LineSegment(p0, p1);
105147           var offsetL = new LineSegment();
105148           this.computeOffsetSegment(seg, Position.LEFT, this._distance, offsetL);
105149           var offsetR = new LineSegment();
105150           this.computeOffsetSegment(seg, Position.RIGHT, this._distance, offsetR);
105151           var dx = p1.x - p0.x;
105152           var dy = p1.y - p0.y;
105153           var angle = Math.atan2(dy, dx);
105154           switch (this._bufParams.getEndCapStyle()) {
105155             case BufferParameters.CAP_ROUND:
105156               this._segList.addPt(offsetL.p1);
105157               this.addFilletArc(p1, angle + Math.PI / 2, angle - Math.PI / 2, CGAlgorithms.CLOCKWISE, this._distance);
105158               this._segList.addPt(offsetR.p1);
105159               break
105160             case BufferParameters.CAP_FLAT:
105161               this._segList.addPt(offsetL.p1);
105162               this._segList.addPt(offsetR.p1);
105163               break
105164             case BufferParameters.CAP_SQUARE:
105165               var squareCapSideOffset = new Coordinate();
105166               squareCapSideOffset.x = Math.abs(this._distance) * Math.cos(angle);
105167               squareCapSideOffset.y = Math.abs(this._distance) * Math.sin(angle);
105168               var squareCapLOffset = new Coordinate(offsetL.p1.x + squareCapSideOffset.x, offsetL.p1.y + squareCapSideOffset.y);
105169               var squareCapROffset = new Coordinate(offsetR.p1.x + squareCapSideOffset.x, offsetR.p1.y + squareCapSideOffset.y);
105170               this._segList.addPt(squareCapLOffset);
105171               this._segList.addPt(squareCapROffset);
105172               break
105173           }
105174         };
105175         OffsetSegmentGenerator.prototype.getCoordinates = function getCoordinates () {
105176           var pts = this._segList.getCoordinates();
105177           return pts
105178         };
105179         OffsetSegmentGenerator.prototype.addMitreJoin = function addMitreJoin (p, offset0, offset1, distance) {
105180           var isMitreWithinLimit = true;
105181           var intPt = null;
105182           try {
105183             intPt = HCoordinate.intersection(offset0.p0, offset0.p1, offset1.p0, offset1.p1);
105184             var mitreRatio = distance <= 0.0 ? 1.0 : intPt.distance(p) / Math.abs(distance);
105185             if (mitreRatio > this._bufParams.getMitreLimit()) { isMitreWithinLimit = false; }
105186           } catch (ex) {
105187             if (ex instanceof NotRepresentableException) {
105188               intPt = new Coordinate(0, 0);
105189               isMitreWithinLimit = false;
105190             } else { throw ex }
105191           } finally {}
105192           if (isMitreWithinLimit) {
105193             this._segList.addPt(intPt);
105194           } else {
105195             this.addLimitedMitreJoin(offset0, offset1, distance, this._bufParams.getMitreLimit());
105196           }
105197         };
105198         OffsetSegmentGenerator.prototype.addFilletCorner = function addFilletCorner (p, p0, p1, direction, radius) {
105199           var dx0 = p0.x - p.x;
105200           var dy0 = p0.y - p.y;
105201           var startAngle = Math.atan2(dy0, dx0);
105202           var dx1 = p1.x - p.x;
105203           var dy1 = p1.y - p.y;
105204           var endAngle = Math.atan2(dy1, dx1);
105205           if (direction === CGAlgorithms.CLOCKWISE) {
105206             if (startAngle <= endAngle) { startAngle += 2.0 * Math.PI; }
105207           } else {
105208             if (startAngle >= endAngle) { startAngle -= 2.0 * Math.PI; }
105209           }
105210           this._segList.addPt(p0);
105211           this.addFilletArc(p, startAngle, endAngle, direction, radius);
105212           this._segList.addPt(p1);
105213         };
105214         OffsetSegmentGenerator.prototype.addOutsideTurn = function addOutsideTurn (orientation, addStartPoint) {
105215           if (this._offset0.p1.distance(this._offset1.p0) < this._distance * OffsetSegmentGenerator.OFFSET_SEGMENT_SEPARATION_FACTOR) {
105216             this._segList.addPt(this._offset0.p1);
105217             return null
105218           }
105219           if (this._bufParams.getJoinStyle() === BufferParameters.JOIN_MITRE) {
105220             this.addMitreJoin(this._s1, this._offset0, this._offset1, this._distance);
105221           } else if (this._bufParams.getJoinStyle() === BufferParameters.JOIN_BEVEL) {
105222             this.addBevelJoin(this._offset0, this._offset1);
105223           } else {
105224             if (addStartPoint) { this._segList.addPt(this._offset0.p1); }
105225             this.addFilletCorner(this._s1, this._offset0.p1, this._offset1.p0, orientation, this._distance);
105226             this._segList.addPt(this._offset1.p0);
105227           }
105228         };
105229         OffsetSegmentGenerator.prototype.createSquare = function createSquare (p) {
105230           this._segList.addPt(new Coordinate(p.x + this._distance, p.y + this._distance));
105231           this._segList.addPt(new Coordinate(p.x + this._distance, p.y - this._distance));
105232           this._segList.addPt(new Coordinate(p.x - this._distance, p.y - this._distance));
105233           this._segList.addPt(new Coordinate(p.x - this._distance, p.y + this._distance));
105234           this._segList.closeRing();
105235         };
105236         OffsetSegmentGenerator.prototype.addSegments = function addSegments (pt, isForward) {
105237           this._segList.addPts(pt, isForward);
105238         };
105239         OffsetSegmentGenerator.prototype.addFirstSegment = function addFirstSegment () {
105240           this._segList.addPt(this._offset1.p0);
105241         };
105242         OffsetSegmentGenerator.prototype.addLastSegment = function addLastSegment () {
105243           this._segList.addPt(this._offset1.p1);
105244         };
105245         OffsetSegmentGenerator.prototype.initSideSegments = function initSideSegments (s1, s2, side) {
105246           this._s1 = s1;
105247           this._s2 = s2;
105248           this._side = side;
105249           this._seg1.setCoordinates(s1, s2);
105250           this.computeOffsetSegment(this._seg1, side, this._distance, this._offset1);
105251         };
105252         OffsetSegmentGenerator.prototype.addLimitedMitreJoin = function addLimitedMitreJoin (offset0, offset1, distance, mitreLimit) {
105253           var basePt = this._seg0.p1;
105254           var ang0 = Angle.angle(basePt, this._seg0.p0);
105255           // const ang1 = Angle.angle(basePt, this._seg1.p1)
105256           var angDiff = Angle.angleBetweenOriented(this._seg0.p0, basePt, this._seg1.p1);
105257           var angDiffHalf = angDiff / 2;
105258           var midAng = Angle.normalize(ang0 + angDiffHalf);
105259           var mitreMidAng = Angle.normalize(midAng + Math.PI);
105260           var mitreDist = mitreLimit * distance;
105261           var bevelDelta = mitreDist * Math.abs(Math.sin(angDiffHalf));
105262           var bevelHalfLen = distance - bevelDelta;
105263           var bevelMidX = basePt.x + mitreDist * Math.cos(mitreMidAng);
105264           var bevelMidY = basePt.y + mitreDist * Math.sin(mitreMidAng);
105265           var bevelMidPt = new Coordinate(bevelMidX, bevelMidY);
105266           var mitreMidLine = new LineSegment(basePt, bevelMidPt);
105267           var bevelEndLeft = mitreMidLine.pointAlongOffset(1.0, bevelHalfLen);
105268           var bevelEndRight = mitreMidLine.pointAlongOffset(1.0, -bevelHalfLen);
105269           if (this._side === Position.LEFT) {
105270             this._segList.addPt(bevelEndLeft);
105271             this._segList.addPt(bevelEndRight);
105272           } else {
105273             this._segList.addPt(bevelEndRight);
105274             this._segList.addPt(bevelEndLeft);
105275           }
105276         };
105277         OffsetSegmentGenerator.prototype.computeOffsetSegment = function computeOffsetSegment (seg, side, distance, offset) {
105278           var sideSign = side === Position.LEFT ? 1 : -1;
105279           var dx = seg.p1.x - seg.p0.x;
105280           var dy = seg.p1.y - seg.p0.y;
105281           var len = Math.sqrt(dx * dx + dy * dy);
105282           var ux = sideSign * distance * dx / len;
105283           var uy = sideSign * distance * dy / len;
105284           offset.p0.x = seg.p0.x - uy;
105285           offset.p0.y = seg.p0.y + ux;
105286           offset.p1.x = seg.p1.x - uy;
105287           offset.p1.y = seg.p1.y + ux;
105288         };
105289         OffsetSegmentGenerator.prototype.addFilletArc = function addFilletArc (p, startAngle, endAngle, direction, radius) {
105290             var this$1 = this;
105291
105292           var directionFactor = direction === CGAlgorithms.CLOCKWISE ? -1 : 1;
105293           var totalAngle = Math.abs(startAngle - endAngle);
105294           var nSegs = Math.trunc(totalAngle / this._filletAngleQuantum + 0.5);
105295           if (nSegs < 1) { return null }
105296           var initAngle = 0.0;
105297           var currAngleInc = totalAngle / nSegs;
105298           var currAngle = initAngle;
105299           var pt = new Coordinate();
105300           while (currAngle < totalAngle) {
105301             var angle = startAngle + directionFactor * currAngle;
105302             pt.x = p.x + radius * Math.cos(angle);
105303             pt.y = p.y + radius * Math.sin(angle);
105304             this$1._segList.addPt(pt);
105305             currAngle += currAngleInc;
105306           }
105307         };
105308         OffsetSegmentGenerator.prototype.addInsideTurn = function addInsideTurn (orientation, addStartPoint) {
105309           this._li.computeIntersection(this._offset0.p0, this._offset0.p1, this._offset1.p0, this._offset1.p1);
105310           if (this._li.hasIntersection()) {
105311             this._segList.addPt(this._li.getIntersection(0));
105312           } else {
105313             this._hasNarrowConcaveAngle = true;
105314             if (this._offset0.p1.distance(this._offset1.p0) < this._distance * OffsetSegmentGenerator.INSIDE_TURN_VERTEX_SNAP_DISTANCE_FACTOR) {
105315               this._segList.addPt(this._offset0.p1);
105316             } else {
105317               this._segList.addPt(this._offset0.p1);
105318               if (this._closingSegLengthFactor > 0) {
105319                 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));
105320                 this._segList.addPt(mid0);
105321                 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));
105322                 this._segList.addPt(mid1);
105323               } else {
105324                 this._segList.addPt(this._s1);
105325               }
105326               this._segList.addPt(this._offset1.p0);
105327             }
105328           }
105329         };
105330         OffsetSegmentGenerator.prototype.createCircle = function createCircle (p) {
105331           var pt = new Coordinate(p.x + this._distance, p.y);
105332           this._segList.addPt(pt);
105333           this.addFilletArc(p, 0.0, 2.0 * Math.PI, -1, this._distance);
105334           this._segList.closeRing();
105335         };
105336         OffsetSegmentGenerator.prototype.addBevelJoin = function addBevelJoin (offset0, offset1) {
105337           this._segList.addPt(offset0.p1);
105338           this._segList.addPt(offset1.p0);
105339         };
105340         OffsetSegmentGenerator.prototype.init = function init (distance) {
105341           this._distance = distance;
105342           this._maxCurveSegmentError = distance * (1 - Math.cos(this._filletAngleQuantum / 2.0));
105343           this._segList = new OffsetSegmentString();
105344           this._segList.setPrecisionModel(this._precisionModel);
105345           this._segList.setMinimumVertexDistance(distance * OffsetSegmentGenerator.CURVE_VERTEX_SNAP_DISTANCE_FACTOR);
105346         };
105347         OffsetSegmentGenerator.prototype.addCollinear = function addCollinear (addStartPoint) {
105348           this._li.computeIntersection(this._s0, this._s1, this._s1, this._s2);
105349           var numInt = this._li.getIntersectionNum();
105350           if (numInt >= 2) {
105351             if (this._bufParams.getJoinStyle() === BufferParameters.JOIN_BEVEL || this._bufParams.getJoinStyle() === BufferParameters.JOIN_MITRE) {
105352               if (addStartPoint) { this._segList.addPt(this._offset0.p1); }
105353               this._segList.addPt(this._offset1.p0);
105354             } else {
105355               this.addFilletCorner(this._s1, this._offset0.p1, this._offset1.p0, CGAlgorithms.CLOCKWISE, this._distance);
105356             }
105357           }
105358         };
105359         OffsetSegmentGenerator.prototype.closeRing = function closeRing () {
105360           this._segList.closeRing();
105361         };
105362         OffsetSegmentGenerator.prototype.hasNarrowConcaveAngle = function hasNarrowConcaveAngle () {
105363           return this._hasNarrowConcaveAngle
105364         };
105365         OffsetSegmentGenerator.prototype.interfaces_ = function interfaces_ () {
105366           return []
105367         };
105368         OffsetSegmentGenerator.prototype.getClass = function getClass () {
105369           return OffsetSegmentGenerator
105370         };
105371         staticAccessors$27.OFFSET_SEGMENT_SEPARATION_FACTOR.get = function () { return 1.0E-3 };
105372         staticAccessors$27.INSIDE_TURN_VERTEX_SNAP_DISTANCE_FACTOR.get = function () { return 1.0E-3 };
105373         staticAccessors$27.CURVE_VERTEX_SNAP_DISTANCE_FACTOR.get = function () { return 1.0E-6 };
105374         staticAccessors$27.MAX_CLOSING_SEG_LEN_FACTOR.get = function () { return 80 };
105375
105376         Object.defineProperties( OffsetSegmentGenerator, staticAccessors$27 );
105377
105378         var OffsetCurveBuilder = function OffsetCurveBuilder () {
105379           this._distance = 0.0;
105380           this._precisionModel = null;
105381           this._bufParams = null;
105382           var precisionModel = arguments[0];
105383           var bufParams = arguments[1];
105384           this._precisionModel = precisionModel;
105385           this._bufParams = bufParams;
105386         };
105387         OffsetCurveBuilder.prototype.getOffsetCurve = function getOffsetCurve (inputPts, distance) {
105388           this._distance = distance;
105389           if (distance === 0.0) { return null }
105390           var isRightSide = distance < 0.0;
105391           var posDistance = Math.abs(distance);
105392           var segGen = this.getSegGen(posDistance);
105393           if (inputPts.length <= 1) {
105394             this.computePointCurve(inputPts[0], segGen);
105395           } else {
105396             this.computeOffsetCurve(inputPts, isRightSide, segGen);
105397           }
105398           var curvePts = segGen.getCoordinates();
105399           if (isRightSide) { CoordinateArrays.reverse(curvePts); }
105400           return curvePts
105401         };
105402         OffsetCurveBuilder.prototype.computeSingleSidedBufferCurve = function computeSingleSidedBufferCurve (inputPts, isRightSide, segGen) {
105403           var distTol = this.simplifyTolerance(this._distance);
105404           if (isRightSide) {
105405             segGen.addSegments(inputPts, true);
105406             var simp2 = BufferInputLineSimplifier.simplify(inputPts, -distTol);
105407             var n2 = simp2.length - 1;
105408             segGen.initSideSegments(simp2[n2], simp2[n2 - 1], Position.LEFT);
105409             segGen.addFirstSegment();
105410             for (var i = n2 - 2; i >= 0; i--) {
105411               segGen.addNextSegment(simp2[i], true);
105412             }
105413           } else {
105414             segGen.addSegments(inputPts, false);
105415             var simp1 = BufferInputLineSimplifier.simplify(inputPts, distTol);
105416             var n1 = simp1.length - 1;
105417             segGen.initSideSegments(simp1[0], simp1[1], Position.LEFT);
105418             segGen.addFirstSegment();
105419             for (var i$1 = 2; i$1 <= n1; i$1++) {
105420               segGen.addNextSegment(simp1[i$1], true);
105421             }
105422           }
105423           segGen.addLastSegment();
105424           segGen.closeRing();
105425         };
105426         OffsetCurveBuilder.prototype.computeRingBufferCurve = function computeRingBufferCurve (inputPts, side, segGen) {
105427           var distTol = this.simplifyTolerance(this._distance);
105428           if (side === Position.RIGHT) { distTol = -distTol; }
105429           var simp = BufferInputLineSimplifier.simplify(inputPts, distTol);
105430           var n = simp.length - 1;
105431           segGen.initSideSegments(simp[n - 1], simp[0], side);
105432           for (var i = 1; i <= n; i++) {
105433             var addStartPoint = i !== 1;
105434             segGen.addNextSegment(simp[i], addStartPoint);
105435           }
105436           segGen.closeRing();
105437         };
105438         OffsetCurveBuilder.prototype.computeLineBufferCurve = function computeLineBufferCurve (inputPts, segGen) {
105439           var distTol = this.simplifyTolerance(this._distance);
105440           var simp1 = BufferInputLineSimplifier.simplify(inputPts, distTol);
105441           var n1 = simp1.length - 1;
105442           segGen.initSideSegments(simp1[0], simp1[1], Position.LEFT);
105443           for (var i = 2; i <= n1; i++) {
105444             segGen.addNextSegment(simp1[i], true);
105445           }
105446           segGen.addLastSegment();
105447           segGen.addLineEndCap(simp1[n1 - 1], simp1[n1]);
105448           var simp2 = BufferInputLineSimplifier.simplify(inputPts, -distTol);
105449           var n2 = simp2.length - 1;
105450           segGen.initSideSegments(simp2[n2], simp2[n2 - 1], Position.LEFT);
105451           for (var i$1 = n2 - 2; i$1 >= 0; i$1--) {
105452             segGen.addNextSegment(simp2[i$1], true);
105453           }
105454           segGen.addLastSegment();
105455           segGen.addLineEndCap(simp2[1], simp2[0]);
105456           segGen.closeRing();
105457         };
105458         OffsetCurveBuilder.prototype.computePointCurve = function computePointCurve (pt, segGen) {
105459           switch (this._bufParams.getEndCapStyle()) {
105460             case BufferParameters.CAP_ROUND:
105461               segGen.createCircle(pt);
105462               break
105463             case BufferParameters.CAP_SQUARE:
105464               segGen.createSquare(pt);
105465               break
105466           }
105467         };
105468         OffsetCurveBuilder.prototype.getLineCurve = function getLineCurve (inputPts, distance) {
105469           this._distance = distance;
105470           if (distance < 0.0 && !this._bufParams.isSingleSided()) { return null }
105471           if (distance === 0.0) { return null }
105472           var posDistance = Math.abs(distance);
105473           var segGen = this.getSegGen(posDistance);
105474           if (inputPts.length <= 1) {
105475             this.computePointCurve(inputPts[0], segGen);
105476           } else {
105477             if (this._bufParams.isSingleSided()) {
105478               var isRightSide = distance < 0.0;
105479               this.computeSingleSidedBufferCurve(inputPts, isRightSide, segGen);
105480             } else { this.computeLineBufferCurve(inputPts, segGen); }
105481           }
105482           var lineCoord = segGen.getCoordinates();
105483           return lineCoord
105484         };
105485         OffsetCurveBuilder.prototype.getBufferParameters = function getBufferParameters () {
105486           return this._bufParams
105487         };
105488         OffsetCurveBuilder.prototype.simplifyTolerance = function simplifyTolerance (bufDistance) {
105489           return bufDistance * this._bufParams.getSimplifyFactor()
105490         };
105491         OffsetCurveBuilder.prototype.getRingCurve = function getRingCurve (inputPts, side, distance) {
105492           this._distance = distance;
105493           if (inputPts.length <= 2) { return this.getLineCurve(inputPts, distance) }
105494           if (distance === 0.0) {
105495             return OffsetCurveBuilder.copyCoordinates(inputPts)
105496           }
105497           var segGen = this.getSegGen(distance);
105498           this.computeRingBufferCurve(inputPts, side, segGen);
105499           return segGen.getCoordinates()
105500         };
105501         OffsetCurveBuilder.prototype.computeOffsetCurve = function computeOffsetCurve (inputPts, isRightSide, segGen) {
105502           var distTol = this.simplifyTolerance(this._distance);
105503           if (isRightSide) {
105504             var simp2 = BufferInputLineSimplifier.simplify(inputPts, -distTol);
105505             var n2 = simp2.length - 1;
105506             segGen.initSideSegments(simp2[n2], simp2[n2 - 1], Position.LEFT);
105507             segGen.addFirstSegment();
105508             for (var i = n2 - 2; i >= 0; i--) {
105509               segGen.addNextSegment(simp2[i], true);
105510             }
105511           } else {
105512             var simp1 = BufferInputLineSimplifier.simplify(inputPts, distTol);
105513             var n1 = simp1.length - 1;
105514             segGen.initSideSegments(simp1[0], simp1[1], Position.LEFT);
105515             segGen.addFirstSegment();
105516             for (var i$1 = 2; i$1 <= n1; i$1++) {
105517               segGen.addNextSegment(simp1[i$1], true);
105518             }
105519           }
105520           segGen.addLastSegment();
105521         };
105522         OffsetCurveBuilder.prototype.getSegGen = function getSegGen (distance) {
105523           return new OffsetSegmentGenerator(this._precisionModel, this._bufParams, distance)
105524         };
105525         OffsetCurveBuilder.prototype.interfaces_ = function interfaces_ () {
105526           return []
105527         };
105528         OffsetCurveBuilder.prototype.getClass = function getClass () {
105529           return OffsetCurveBuilder
105530         };
105531         OffsetCurveBuilder.copyCoordinates = function copyCoordinates (pts) {
105532           var copy = new Array(pts.length).fill(null);
105533           for (var i = 0; i < copy.length; i++) {
105534             copy[i] = new Coordinate(pts[i]);
105535           }
105536           return copy
105537         };
105538
105539         var SubgraphDepthLocater = function SubgraphDepthLocater () {
105540           this._subgraphs = null;
105541           this._seg = new LineSegment();
105542           this._cga = new CGAlgorithms();
105543           var subgraphs = arguments[0];
105544           this._subgraphs = subgraphs;
105545         };
105546
105547         var staticAccessors$30 = { DepthSegment: { configurable: true } };
105548         SubgraphDepthLocater.prototype.findStabbedSegments = function findStabbedSegments () {
105549             var this$1 = this;
105550
105551           if (arguments.length === 1) {
105552             var stabbingRayLeftPt = arguments[0];
105553             var stabbedSegments = new ArrayList();
105554             for (var i = this._subgraphs.iterator(); i.hasNext();) {
105555               var bsg = i.next();
105556               var env = bsg.getEnvelope();
105557               if (stabbingRayLeftPt.y < env.getMinY() || stabbingRayLeftPt.y > env.getMaxY()) { continue }
105558               this$1.findStabbedSegments(stabbingRayLeftPt, bsg.getDirectedEdges(), stabbedSegments);
105559             }
105560             return stabbedSegments
105561           } else if (arguments.length === 3) {
105562             if (hasInterface(arguments[2], List) && (arguments[0] instanceof Coordinate && arguments[1] instanceof DirectedEdge)) {
105563               var stabbingRayLeftPt$1 = arguments[0];
105564               var dirEdge = arguments[1];
105565               var stabbedSegments$1 = arguments[2];
105566               var pts = dirEdge.getEdge().getCoordinates();
105567               for (var i$1 = 0; i$1 < pts.length - 1; i$1++) {
105568                 this$1._seg.p0 = pts[i$1];
105569                 this$1._seg.p1 = pts[i$1 + 1];
105570                 if (this$1._seg.p0.y > this$1._seg.p1.y) { this$1._seg.reverse(); }
105571                 var maxx = Math.max(this$1._seg.p0.x, this$1._seg.p1.x);
105572                 if (maxx < stabbingRayLeftPt$1.x) { continue }
105573                 if (this$1._seg.isHorizontal()) { continue }
105574                 if (stabbingRayLeftPt$1.y < this$1._seg.p0.y || stabbingRayLeftPt$1.y > this$1._seg.p1.y) { continue }
105575                 if (CGAlgorithms.computeOrientation(this$1._seg.p0, this$1._seg.p1, stabbingRayLeftPt$1) === CGAlgorithms.RIGHT) { continue }
105576                 var depth = dirEdge.getDepth(Position.LEFT);
105577                 if (!this$1._seg.p0.equals(pts[i$1])) { depth = dirEdge.getDepth(Position.RIGHT); }
105578                 var ds = new DepthSegment(this$1._seg, depth);
105579                 stabbedSegments$1.add(ds);
105580               }
105581             } else if (hasInterface(arguments[2], List) && (arguments[0] instanceof Coordinate && hasInterface(arguments[1], List))) {
105582               var stabbingRayLeftPt$2 = arguments[0];
105583               var dirEdges = arguments[1];
105584               var stabbedSegments$2 = arguments[2];
105585               for (var i$2 = dirEdges.iterator(); i$2.hasNext();) {
105586                 var de = i$2.next();
105587                 if (!de.isForward()) { continue }
105588                 this$1.findStabbedSegments(stabbingRayLeftPt$2, de, stabbedSegments$2);
105589               }
105590             }
105591           }
105592         };
105593         SubgraphDepthLocater.prototype.getDepth = function getDepth (p) {
105594           var stabbedSegments = this.findStabbedSegments(p);
105595           if (stabbedSegments.size() === 0) { return 0 }
105596           var ds = Collections.min(stabbedSegments);
105597           return ds._leftDepth
105598         };
105599         SubgraphDepthLocater.prototype.interfaces_ = function interfaces_ () {
105600           return []
105601         };
105602         SubgraphDepthLocater.prototype.getClass = function getClass () {
105603           return SubgraphDepthLocater
105604         };
105605         staticAccessors$30.DepthSegment.get = function () { return DepthSegment };
105606
105607         Object.defineProperties( SubgraphDepthLocater, staticAccessors$30 );
105608
105609         var DepthSegment = function DepthSegment () {
105610           this._upwardSeg = null;
105611           this._leftDepth = null;
105612           var seg = arguments[0];
105613           var depth = arguments[1];
105614           this._upwardSeg = new LineSegment(seg);
105615           this._leftDepth = depth;
105616         };
105617         DepthSegment.prototype.compareTo = function compareTo (obj) {
105618           var other = obj;
105619           if (this._upwardSeg.minX() >= other._upwardSeg.maxX()) { return 1 }
105620           if (this._upwardSeg.maxX() <= other._upwardSeg.minX()) { return -1 }
105621           var orientIndex = this._upwardSeg.orientationIndex(other._upwardSeg);
105622           if (orientIndex !== 0) { return orientIndex }
105623           orientIndex = -1 * other._upwardSeg.orientationIndex(this._upwardSeg);
105624           if (orientIndex !== 0) { return orientIndex }
105625           return this._upwardSeg.compareTo(other._upwardSeg)
105626         };
105627         DepthSegment.prototype.compareX = function compareX (seg0, seg1) {
105628           var compare0 = seg0.p0.compareTo(seg1.p0);
105629           if (compare0 !== 0) { return compare0 }
105630           return seg0.p1.compareTo(seg1.p1)
105631         };
105632         DepthSegment.prototype.toString = function toString () {
105633           return this._upwardSeg.toString()
105634         };
105635         DepthSegment.prototype.interfaces_ = function interfaces_ () {
105636           return [Comparable]
105637         };
105638         DepthSegment.prototype.getClass = function getClass () {
105639           return DepthSegment
105640         };
105641
105642         var Triangle = function Triangle (p0, p1, p2) {
105643           this.p0 = p0 || null;
105644           this.p1 = p1 || null;
105645           this.p2 = p2 || null;
105646         };
105647         Triangle.prototype.area = function area () {
105648           return Triangle.area(this.p0, this.p1, this.p2)
105649         };
105650         Triangle.prototype.signedArea = function signedArea () {
105651           return Triangle.signedArea(this.p0, this.p1, this.p2)
105652         };
105653         Triangle.prototype.interpolateZ = function interpolateZ (p) {
105654           if (p === null) { throw new IllegalArgumentException('Supplied point is null.') }
105655           return Triangle.interpolateZ(p, this.p0, this.p1, this.p2)
105656         };
105657         Triangle.prototype.longestSideLength = function longestSideLength () {
105658           return Triangle.longestSideLength(this.p0, this.p1, this.p2)
105659         };
105660         Triangle.prototype.isAcute = function isAcute () {
105661           return Triangle.isAcute(this.p0, this.p1, this.p2)
105662         };
105663         Triangle.prototype.circumcentre = function circumcentre () {
105664           return Triangle.circumcentre(this.p0, this.p1, this.p2)
105665         };
105666         Triangle.prototype.area3D = function area3D () {
105667           return Triangle.area3D(this.p0, this.p1, this.p2)
105668         };
105669         Triangle.prototype.centroid = function centroid () {
105670           return Triangle.centroid(this.p0, this.p1, this.p2)
105671         };
105672         Triangle.prototype.inCentre = function inCentre () {
105673           return Triangle.inCentre(this.p0, this.p1, this.p2)
105674         };
105675         Triangle.prototype.interfaces_ = function interfaces_ () {
105676           return []
105677         };
105678         Triangle.prototype.getClass = function getClass () {
105679           return Triangle
105680         };
105681         Triangle.area = function area (a, b, c) {
105682           return Math.abs(((c.x - a.x) * (b.y - a.y) - (b.x - a.x) * (c.y - a.y)) / 2)
105683         };
105684         Triangle.signedArea = function signedArea (a, b, c) {
105685           return ((c.x - a.x) * (b.y - a.y) - (b.x - a.x) * (c.y - a.y)) / 2
105686         };
105687         Triangle.det = function det (m00, m01, m10, m11) {
105688           return m00 * m11 - m01 * m10
105689         };
105690         Triangle.interpolateZ = function interpolateZ (p, v0, v1, v2) {
105691           var x0 = v0.x;
105692           var y0 = v0.y;
105693           var a = v1.x - x0;
105694           var b = v2.x - x0;
105695           var c = v1.y - y0;
105696           var d = v2.y - y0;
105697           var det = a * d - b * c;
105698           var dx = p.x - x0;
105699           var dy = p.y - y0;
105700           var t = (d * dx - b * dy) / det;
105701           var u = (-c * dx + a * dy) / det;
105702           var z = v0.z + t * (v1.z - v0.z) + u * (v2.z - v0.z);
105703           return z
105704         };
105705         Triangle.longestSideLength = function longestSideLength (a, b, c) {
105706           var lenAB = a.distance(b);
105707           var lenBC = b.distance(c);
105708           var lenCA = c.distance(a);
105709           var maxLen = lenAB;
105710           if (lenBC > maxLen) { maxLen = lenBC; }
105711           if (lenCA > maxLen) { maxLen = lenCA; }
105712           return maxLen
105713         };
105714         Triangle.isAcute = function isAcute (a, b, c) {
105715           if (!Angle.isAcute(a, b, c)) { return false }
105716           if (!Angle.isAcute(b, c, a)) { return false }
105717           if (!Angle.isAcute(c, a, b)) { return false }
105718           return true
105719         };
105720         Triangle.circumcentre = function circumcentre (a, b, c) {
105721           var cx = c.x;
105722           var cy = c.y;
105723           var ax = a.x - cx;
105724           var ay = a.y - cy;
105725           var bx = b.x - cx;
105726           var by = b.y - cy;
105727           var denom = 2 * Triangle.det(ax, ay, bx, by);
105728           var numx = Triangle.det(ay, ax * ax + ay * ay, by, bx * bx + by * by);
105729           var numy = Triangle.det(ax, ax * ax + ay * ay, bx, bx * bx + by * by);
105730           var ccx = cx - numx / denom;
105731           var ccy = cy + numy / denom;
105732           return new Coordinate(ccx, ccy)
105733         };
105734         Triangle.perpendicularBisector = function perpendicularBisector (a, b) {
105735           var dx = b.x - a.x;
105736           var dy = b.y - a.y;
105737           var l1 = new HCoordinate(a.x + dx / 2.0, a.y + dy / 2.0, 1.0);
105738           var l2 = new HCoordinate(a.x - dy + dx / 2.0, a.y + dx + dy / 2.0, 1.0);
105739           return new HCoordinate(l1, l2)
105740         };
105741         Triangle.angleBisector = function angleBisector (a, b, c) {
105742           var len0 = b.distance(a);
105743           var len2 = b.distance(c);
105744           var frac = len0 / (len0 + len2);
105745           var dx = c.x - a.x;
105746           var dy = c.y - a.y;
105747           var splitPt = new Coordinate(a.x + frac * dx, a.y + frac * dy);
105748           return splitPt
105749         };
105750         Triangle.area3D = function area3D (a, b, c) {
105751           var ux = b.x - a.x;
105752           var uy = b.y - a.y;
105753           var uz = b.z - a.z;
105754           var vx = c.x - a.x;
105755           var vy = c.y - a.y;
105756           var vz = c.z - a.z;
105757           var crossx = uy * vz - uz * vy;
105758           var crossy = uz * vx - ux * vz;
105759           var crossz = ux * vy - uy * vx;
105760           var absSq = crossx * crossx + crossy * crossy + crossz * crossz;
105761           var area3D = Math.sqrt(absSq) / 2;
105762           return area3D
105763         };
105764         Triangle.centroid = function centroid (a, b, c) {
105765           var x = (a.x + b.x + c.x) / 3;
105766           var y = (a.y + b.y + c.y) / 3;
105767           return new Coordinate(x, y)
105768         };
105769         Triangle.inCentre = function inCentre (a, b, c) {
105770           var len0 = b.distance(c);
105771           var len1 = a.distance(c);
105772           var len2 = a.distance(b);
105773           var circum = len0 + len1 + len2;
105774           var inCentreX = (len0 * a.x + len1 * b.x + len2 * c.x) / circum;
105775           var inCentreY = (len0 * a.y + len1 * b.y + len2 * c.y) / circum;
105776           return new Coordinate(inCentreX, inCentreY)
105777         };
105778
105779         var OffsetCurveSetBuilder = function OffsetCurveSetBuilder () {
105780           this._inputGeom = null;
105781           this._distance = null;
105782           this._curveBuilder = null;
105783           this._curveList = new ArrayList();
105784           var inputGeom = arguments[0];
105785           var distance = arguments[1];
105786           var curveBuilder = arguments[2];
105787           this._inputGeom = inputGeom;
105788           this._distance = distance;
105789           this._curveBuilder = curveBuilder;
105790         };
105791         OffsetCurveSetBuilder.prototype.addPoint = function addPoint (p) {
105792           if (this._distance <= 0.0) { return null }
105793           var coord = p.getCoordinates();
105794           var curve = this._curveBuilder.getLineCurve(coord, this._distance);
105795           this.addCurve(curve, Location.EXTERIOR, Location.INTERIOR);
105796         };
105797         OffsetCurveSetBuilder.prototype.addPolygon = function addPolygon (p) {
105798             var this$1 = this;
105799
105800           var offsetDistance = this._distance;
105801           var offsetSide = Position.LEFT;
105802           if (this._distance < 0.0) {
105803             offsetDistance = -this._distance;
105804             offsetSide = Position.RIGHT;
105805           }
105806           var shell = p.getExteriorRing();
105807           var shellCoord = CoordinateArrays.removeRepeatedPoints(shell.getCoordinates());
105808           if (this._distance < 0.0 && this.isErodedCompletely(shell, this._distance)) { return null }
105809           if (this._distance <= 0.0 && shellCoord.length < 3) { return null }
105810           this.addPolygonRing(shellCoord, offsetDistance, offsetSide, Location.EXTERIOR, Location.INTERIOR);
105811           for (var i = 0; i < p.getNumInteriorRing(); i++) {
105812             var hole = p.getInteriorRingN(i);
105813             var holeCoord = CoordinateArrays.removeRepeatedPoints(hole.getCoordinates());
105814             if (this$1._distance > 0.0 && this$1.isErodedCompletely(hole, -this$1._distance)) { continue }
105815             this$1.addPolygonRing(holeCoord, offsetDistance, Position.opposite(offsetSide), Location.INTERIOR, Location.EXTERIOR);
105816           }
105817         };
105818         OffsetCurveSetBuilder.prototype.isTriangleErodedCompletely = function isTriangleErodedCompletely (triangleCoord, bufferDistance) {
105819           var tri = new Triangle(triangleCoord[0], triangleCoord[1], triangleCoord[2]);
105820           var inCentre = tri.inCentre();
105821           var distToCentre = CGAlgorithms.distancePointLine(inCentre, tri.p0, tri.p1);
105822           return distToCentre < Math.abs(bufferDistance)
105823         };
105824         OffsetCurveSetBuilder.prototype.addLineString = function addLineString (line) {
105825           if (this._distance <= 0.0 && !this._curveBuilder.getBufferParameters().isSingleSided()) { return null }
105826           var coord = CoordinateArrays.removeRepeatedPoints(line.getCoordinates());
105827           var curve = this._curveBuilder.getLineCurve(coord, this._distance);
105828           this.addCurve(curve, Location.EXTERIOR, Location.INTERIOR);
105829         };
105830         OffsetCurveSetBuilder.prototype.addCurve = function addCurve (coord, leftLoc, rightLoc) {
105831           if (coord === null || coord.length < 2) { return null }
105832           var e = new NodedSegmentString(coord, new Label(0, Location.BOUNDARY, leftLoc, rightLoc));
105833           this._curveList.add(e);
105834         };
105835         OffsetCurveSetBuilder.prototype.getCurves = function getCurves () {
105836           this.add(this._inputGeom);
105837           return this._curveList
105838         };
105839         OffsetCurveSetBuilder.prototype.addPolygonRing = function addPolygonRing (coord, offsetDistance, side, cwLeftLoc, cwRightLoc) {
105840           if (offsetDistance === 0.0 && coord.length < LinearRing.MINIMUM_VALID_SIZE) { return null }
105841           var leftLoc = cwLeftLoc;
105842           var rightLoc = cwRightLoc;
105843           if (coord.length >= LinearRing.MINIMUM_VALID_SIZE && CGAlgorithms.isCCW(coord)) {
105844             leftLoc = cwRightLoc;
105845             rightLoc = cwLeftLoc;
105846             side = Position.opposite(side);
105847           }
105848           var curve = this._curveBuilder.getRingCurve(coord, side, offsetDistance);
105849           this.addCurve(curve, leftLoc, rightLoc);
105850         };
105851         OffsetCurveSetBuilder.prototype.add = function add (g) {
105852           if (g.isEmpty()) { return null }
105853           if (g instanceof Polygon) { this.addPolygon(g); }
105854           else if (g instanceof LineString) { this.addLineString(g); }
105855           else if (g instanceof Point$1) { this.addPoint(g); }
105856           else if (g instanceof MultiPoint) { this.addCollection(g); }
105857           else if (g instanceof MultiLineString) { this.addCollection(g); }
105858           else if (g instanceof MultiPolygon) { this.addCollection(g); }
105859           else if (g instanceof GeometryCollection) { this.addCollection(g); }
105860           // else throw new UnsupportedOperationException(g.getClass().getName())
105861         };
105862         OffsetCurveSetBuilder.prototype.isErodedCompletely = function isErodedCompletely (ring, bufferDistance) {
105863           var ringCoord = ring.getCoordinates();
105864           // const minDiam = 0.0
105865           if (ringCoord.length < 4) { return bufferDistance < 0 }
105866           if (ringCoord.length === 4) { return this.isTriangleErodedCompletely(ringCoord, bufferDistance) }
105867           var env = ring.getEnvelopeInternal();
105868           var envMinDimension = Math.min(env.getHeight(), env.getWidth());
105869           if (bufferDistance < 0.0 && 2 * Math.abs(bufferDistance) > envMinDimension) { return true }
105870           return false
105871         };
105872         OffsetCurveSetBuilder.prototype.addCollection = function addCollection (gc) {
105873             var this$1 = this;
105874
105875           for (var i = 0; i < gc.getNumGeometries(); i++) {
105876             var g = gc.getGeometryN(i);
105877             this$1.add(g);
105878           }
105879         };
105880         OffsetCurveSetBuilder.prototype.interfaces_ = function interfaces_ () {
105881           return []
105882         };
105883         OffsetCurveSetBuilder.prototype.getClass = function getClass () {
105884           return OffsetCurveSetBuilder
105885         };
105886
105887         var PointOnGeometryLocator = function PointOnGeometryLocator () {};
105888
105889         PointOnGeometryLocator.prototype.locate = function locate (p) {};
105890         PointOnGeometryLocator.prototype.interfaces_ = function interfaces_ () {
105891           return []
105892         };
105893         PointOnGeometryLocator.prototype.getClass = function getClass () {
105894           return PointOnGeometryLocator
105895         };
105896
105897         var GeometryCollectionIterator = function GeometryCollectionIterator () {
105898           this._parent = null;
105899           this._atStart = null;
105900           this._max = null;
105901           this._index = null;
105902           this._subcollectionIterator = null;
105903           var parent = arguments[0];
105904           this._parent = parent;
105905           this._atStart = true;
105906           this._index = 0;
105907           this._max = parent.getNumGeometries();
105908         };
105909         GeometryCollectionIterator.prototype.next = function next () {
105910           if (this._atStart) {
105911             this._atStart = false;
105912             if (GeometryCollectionIterator.isAtomic(this._parent)) { this._index++; }
105913             return this._parent
105914           }
105915           if (this._subcollectionIterator !== null) {
105916             if (this._subcollectionIterator.hasNext()) {
105917               return this._subcollectionIterator.next()
105918             } else {
105919               this._subcollectionIterator = null;
105920             }
105921           }
105922           if (this._index >= this._max) {
105923             throw new NoSuchElementException()
105924           }
105925           var obj = this._parent.getGeometryN(this._index++);
105926           if (obj instanceof GeometryCollection) {
105927             this._subcollectionIterator = new GeometryCollectionIterator(obj);
105928             return this._subcollectionIterator.next()
105929           }
105930           return obj
105931         };
105932         GeometryCollectionIterator.prototype.remove = function remove () {
105933           throw new Error(this.getClass().getName())
105934         };
105935         GeometryCollectionIterator.prototype.hasNext = function hasNext () {
105936           if (this._atStart) {
105937             return true
105938           }
105939           if (this._subcollectionIterator !== null) {
105940             if (this._subcollectionIterator.hasNext()) {
105941               return true
105942             }
105943             this._subcollectionIterator = null;
105944           }
105945           if (this._index >= this._max) {
105946             return false
105947           }
105948           return true
105949         };
105950         GeometryCollectionIterator.prototype.interfaces_ = function interfaces_ () {
105951           return [Iterator$1]
105952         };
105953         GeometryCollectionIterator.prototype.getClass = function getClass () {
105954           return GeometryCollectionIterator
105955         };
105956         GeometryCollectionIterator.isAtomic = function isAtomic (geom) {
105957           return !(geom instanceof GeometryCollection)
105958         };
105959
105960         var SimplePointInAreaLocator = function SimplePointInAreaLocator () {
105961           this._geom = null;
105962           var geom = arguments[0];
105963           this._geom = geom;
105964         };
105965         SimplePointInAreaLocator.prototype.locate = function locate (p) {
105966           return SimplePointInAreaLocator.locate(p, this._geom)
105967         };
105968         SimplePointInAreaLocator.prototype.interfaces_ = function interfaces_ () {
105969           return [PointOnGeometryLocator]
105970         };
105971         SimplePointInAreaLocator.prototype.getClass = function getClass () {
105972           return SimplePointInAreaLocator
105973         };
105974         SimplePointInAreaLocator.isPointInRing = function isPointInRing (p, ring) {
105975           if (!ring.getEnvelopeInternal().intersects(p)) { return false }
105976           return CGAlgorithms.isPointInRing(p, ring.getCoordinates())
105977         };
105978         SimplePointInAreaLocator.containsPointInPolygon = function containsPointInPolygon (p, poly) {
105979           if (poly.isEmpty()) { return false }
105980           var shell = poly.getExteriorRing();
105981           if (!SimplePointInAreaLocator.isPointInRing(p, shell)) { return false }
105982           for (var i = 0; i < poly.getNumInteriorRing(); i++) {
105983             var hole = poly.getInteriorRingN(i);
105984             if (SimplePointInAreaLocator.isPointInRing(p, hole)) { return false }
105985           }
105986           return true
105987         };
105988         SimplePointInAreaLocator.containsPoint = function containsPoint (p, geom) {
105989           if (geom instanceof Polygon) {
105990             return SimplePointInAreaLocator.containsPointInPolygon(p, geom)
105991           } else if (geom instanceof GeometryCollection) {
105992             var geomi = new GeometryCollectionIterator(geom);
105993             while (geomi.hasNext()) {
105994               var g2 = geomi.next();
105995               if (g2 !== geom) { if (SimplePointInAreaLocator.containsPoint(p, g2)) { return true } }
105996             }
105997           }
105998           return false
105999         };
106000         SimplePointInAreaLocator.locate = function locate (p, geom) {
106001           if (geom.isEmpty()) { return Location.EXTERIOR }
106002           if (SimplePointInAreaLocator.containsPoint(p, geom)) { return Location.INTERIOR }
106003           return Location.EXTERIOR
106004         };
106005
106006         var EdgeEndStar = function EdgeEndStar () {
106007           this._edgeMap = new TreeMap();
106008           this._edgeList = null;
106009           this._ptInAreaLocation = [Location.NONE, Location.NONE];
106010         };
106011         EdgeEndStar.prototype.getNextCW = function getNextCW (ee) {
106012           this.getEdges();
106013           var i = this._edgeList.indexOf(ee);
106014           var iNextCW = i - 1;
106015           if (i === 0) { iNextCW = this._edgeList.size() - 1; }
106016           return this._edgeList.get(iNextCW)
106017         };
106018         EdgeEndStar.prototype.propagateSideLabels = function propagateSideLabels (geomIndex) {
106019           var startLoc = Location.NONE;
106020           for (var it = this.iterator(); it.hasNext();) {
106021             var e = it.next();
106022             var label = e.getLabel();
106023             if (label.isArea(geomIndex) && label.getLocation(geomIndex, Position.LEFT) !== Location.NONE) { startLoc = label.getLocation(geomIndex, Position.LEFT); }
106024           }
106025           if (startLoc === Location.NONE) { return null }
106026           var currLoc = startLoc;
106027           for (var it$1 = this.iterator(); it$1.hasNext();) {
106028             var e$1 = it$1.next();
106029             var label$1 = e$1.getLabel();
106030             if (label$1.getLocation(geomIndex, Position.ON) === Location.NONE) { label$1.setLocation(geomIndex, Position.ON, currLoc); }
106031             if (label$1.isArea(geomIndex)) {
106032               var leftLoc = label$1.getLocation(geomIndex, Position.LEFT);
106033               var rightLoc = label$1.getLocation(geomIndex, Position.RIGHT);
106034               if (rightLoc !== Location.NONE) {
106035                 if (rightLoc !== currLoc) { throw new TopologyException('side location conflict', e$1.getCoordinate()) }
106036                 if (leftLoc === Location.NONE) {
106037                   Assert.shouldNeverReachHere('found single null side (at ' + e$1.getCoordinate() + ')');
106038                 }
106039                 currLoc = leftLoc;
106040               } else {
106041                 Assert.isTrue(label$1.getLocation(geomIndex, Position.LEFT) === Location.NONE, 'found single null side');
106042                 label$1.setLocation(geomIndex, Position.RIGHT, currLoc);
106043                 label$1.setLocation(geomIndex, Position.LEFT, currLoc);
106044               }
106045             }
106046           }
106047         };
106048         EdgeEndStar.prototype.getCoordinate = function getCoordinate () {
106049           var it = this.iterator();
106050           if (!it.hasNext()) { return null }
106051           var e = it.next();
106052           return e.getCoordinate()
106053         };
106054         EdgeEndStar.prototype.print = function print (out) {
106055           System.out.println('EdgeEndStar:   ' + this.getCoordinate());
106056           for (var it = this.iterator(); it.hasNext();) {
106057             var e = it.next();
106058             e.print(out);
106059           }
106060         };
106061         EdgeEndStar.prototype.isAreaLabelsConsistent = function isAreaLabelsConsistent (geomGraph) {
106062           this.computeEdgeEndLabels(geomGraph.getBoundaryNodeRule());
106063           return this.checkAreaLabelsConsistent(0)
106064         };
106065         EdgeEndStar.prototype.checkAreaLabelsConsistent = function checkAreaLabelsConsistent (geomIndex) {
106066           var edges = this.getEdges();
106067           if (edges.size() <= 0) { return true }
106068           var lastEdgeIndex = edges.size() - 1;
106069           var startLabel = edges.get(lastEdgeIndex).getLabel();
106070           var startLoc = startLabel.getLocation(geomIndex, Position.LEFT);
106071           Assert.isTrue(startLoc !== Location.NONE, 'Found unlabelled area edge');
106072           var currLoc = startLoc;
106073           for (var it = this.iterator(); it.hasNext();) {
106074             var e = it.next();
106075             var label = e.getLabel();
106076             Assert.isTrue(label.isArea(geomIndex), 'Found non-area edge');
106077             var leftLoc = label.getLocation(geomIndex, Position.LEFT);
106078             var rightLoc = label.getLocation(geomIndex, Position.RIGHT);
106079             if (leftLoc === rightLoc) {
106080               return false
106081             }
106082             if (rightLoc !== currLoc) {
106083               return false
106084             }
106085             currLoc = leftLoc;
106086           }
106087           return true
106088         };
106089         EdgeEndStar.prototype.findIndex = function findIndex (eSearch) {
106090             var this$1 = this;
106091
106092           this.iterator();
106093           for (var i = 0; i < this._edgeList.size(); i++) {
106094             var e = this$1._edgeList.get(i);
106095             if (e === eSearch) { return i }
106096           }
106097           return -1
106098         };
106099         EdgeEndStar.prototype.iterator = function iterator () {
106100           return this.getEdges().iterator()
106101         };
106102         EdgeEndStar.prototype.getEdges = function getEdges () {
106103           if (this._edgeList === null) {
106104             this._edgeList = new ArrayList(this._edgeMap.values());
106105           }
106106           return this._edgeList
106107         };
106108         EdgeEndStar.prototype.getLocation = function getLocation (geomIndex, p, geom) {
106109           if (this._ptInAreaLocation[geomIndex] === Location.NONE) {
106110             this._ptInAreaLocation[geomIndex] = SimplePointInAreaLocator.locate(p, geom[geomIndex].getGeometry());
106111           }
106112           return this._ptInAreaLocation[geomIndex]
106113         };
106114         EdgeEndStar.prototype.toString = function toString () {
106115           var buf = new StringBuffer();
106116           buf.append('EdgeEndStar:   ' + this.getCoordinate());
106117           buf.append('\n');
106118           for (var it = this.iterator(); it.hasNext();) {
106119             var e = it.next();
106120             buf.append(e);
106121             buf.append('\n');
106122           }
106123           return buf.toString()
106124         };
106125         EdgeEndStar.prototype.computeEdgeEndLabels = function computeEdgeEndLabels (boundaryNodeRule) {
106126           for (var it = this.iterator(); it.hasNext();) {
106127             var ee = it.next();
106128             ee.computeLabel(boundaryNodeRule);
106129           }
106130         };
106131         EdgeEndStar.prototype.computeLabelling = function computeLabelling (geomGraph) {
106132             var this$1 = this;
106133
106134           this.computeEdgeEndLabels(geomGraph[0].getBoundaryNodeRule());
106135           this.propagateSideLabels(0);
106136           this.propagateSideLabels(1);
106137           var hasDimensionalCollapseEdge = [false, false];
106138           for (var it = this.iterator(); it.hasNext();) {
106139             var e = it.next();
106140             var label = e.getLabel();
106141             for (var geomi = 0; geomi < 2; geomi++) {
106142               if (label.isLine(geomi) && label.getLocation(geomi) === Location.BOUNDARY) { hasDimensionalCollapseEdge[geomi] = true; }
106143             }
106144           }
106145           for (var it$1 = this.iterator(); it$1.hasNext();) {
106146             var e$1 = it$1.next();
106147             var label$1 = e$1.getLabel();
106148             for (var geomi$1 = 0; geomi$1 < 2; geomi$1++) {
106149               if (label$1.isAnyNull(geomi$1)) {
106150                 var loc = Location.NONE;
106151                 if (hasDimensionalCollapseEdge[geomi$1]) {
106152                   loc = Location.EXTERIOR;
106153                 } else {
106154                   var p = e$1.getCoordinate();
106155                   loc = this$1.getLocation(geomi$1, p, geomGraph);
106156                 }
106157                 label$1.setAllLocationsIfNull(geomi$1, loc);
106158               }
106159             }
106160           }
106161         };
106162         EdgeEndStar.prototype.getDegree = function getDegree () {
106163           return this._edgeMap.size()
106164         };
106165         EdgeEndStar.prototype.insertEdgeEnd = function insertEdgeEnd (e, obj) {
106166           this._edgeMap.put(e, obj);
106167           this._edgeList = null;
106168         };
106169         EdgeEndStar.prototype.interfaces_ = function interfaces_ () {
106170           return []
106171         };
106172         EdgeEndStar.prototype.getClass = function getClass () {
106173           return EdgeEndStar
106174         };
106175
106176         var DirectedEdgeStar = (function (EdgeEndStar$$1) {
106177           function DirectedEdgeStar () {
106178             EdgeEndStar$$1.call(this);
106179             this._resultAreaEdgeList = null;
106180             this._label = null;
106181             this._SCANNING_FOR_INCOMING = 1;
106182             this._LINKING_TO_OUTGOING = 2;
106183           }
106184
106185           if ( EdgeEndStar$$1 ) { DirectedEdgeStar.__proto__ = EdgeEndStar$$1; }
106186           DirectedEdgeStar.prototype = Object.create( EdgeEndStar$$1 && EdgeEndStar$$1.prototype );
106187           DirectedEdgeStar.prototype.constructor = DirectedEdgeStar;
106188           DirectedEdgeStar.prototype.linkResultDirectedEdges = function linkResultDirectedEdges () {
106189             var this$1 = this;
106190
106191             this.getResultAreaEdges();
106192             var firstOut = null;
106193             var incoming = null;
106194             var state = this._SCANNING_FOR_INCOMING;
106195             for (var i = 0; i < this._resultAreaEdgeList.size(); i++) {
106196               var nextOut = this$1._resultAreaEdgeList.get(i);
106197               var nextIn = nextOut.getSym();
106198               if (!nextOut.getLabel().isArea()) { continue }
106199               if (firstOut === null && nextOut.isInResult()) { firstOut = nextOut; }
106200               switch (state) {
106201                 case this$1._SCANNING_FOR_INCOMING:
106202                   if (!nextIn.isInResult()) { continue }
106203                   incoming = nextIn;
106204                   state = this$1._LINKING_TO_OUTGOING;
106205                   break
106206                 case this$1._LINKING_TO_OUTGOING:
106207                   if (!nextOut.isInResult()) { continue }
106208                   incoming.setNext(nextOut);
106209                   state = this$1._SCANNING_FOR_INCOMING;
106210                   break
106211               }
106212             }
106213             if (state === this._LINKING_TO_OUTGOING) {
106214               if (firstOut === null) { throw new TopologyException('no outgoing dirEdge found', this.getCoordinate()) }
106215               Assert.isTrue(firstOut.isInResult(), 'unable to link last incoming dirEdge');
106216               incoming.setNext(firstOut);
106217             }
106218           };
106219           DirectedEdgeStar.prototype.insert = function insert (ee) {
106220             var de = ee;
106221             this.insertEdgeEnd(de, de);
106222           };
106223           DirectedEdgeStar.prototype.getRightmostEdge = function getRightmostEdge () {
106224             var edges = this.getEdges();
106225             var size = edges.size();
106226             if (size < 1) { return null }
106227             var de0 = edges.get(0);
106228             if (size === 1) { return de0 }
106229             var deLast = edges.get(size - 1);
106230             var quad0 = de0.getQuadrant();
106231             var quad1 = deLast.getQuadrant();
106232             if (Quadrant.isNorthern(quad0) && Quadrant.isNorthern(quad1)) { return de0; } else if (!Quadrant.isNorthern(quad0) && !Quadrant.isNorthern(quad1)) { return deLast; } else {
106233               // const nonHorizontalEdge = null
106234               if (de0.getDy() !== 0) { return de0; } else if (deLast.getDy() !== 0) { return deLast }
106235             }
106236             Assert.shouldNeverReachHere('found two horizontal edges incident on node');
106237             return null
106238           };
106239           DirectedEdgeStar.prototype.print = function print (out) {
106240             System.out.println('DirectedEdgeStar: ' + this.getCoordinate());
106241             for (var it = this.iterator(); it.hasNext();) {
106242               var de = it.next();
106243               out.print('out ');
106244               de.print(out);
106245               out.println();
106246               out.print('in ');
106247               de.getSym().print(out);
106248               out.println();
106249             }
106250           };
106251           DirectedEdgeStar.prototype.getResultAreaEdges = function getResultAreaEdges () {
106252             var this$1 = this;
106253
106254             if (this._resultAreaEdgeList !== null) { return this._resultAreaEdgeList }
106255             this._resultAreaEdgeList = new ArrayList();
106256             for (var it = this.iterator(); it.hasNext();) {
106257               var de = it.next();
106258               if (de.isInResult() || de.getSym().isInResult()) { this$1._resultAreaEdgeList.add(de); }
106259             }
106260             return this._resultAreaEdgeList
106261           };
106262           DirectedEdgeStar.prototype.updateLabelling = function updateLabelling (nodeLabel) {
106263             for (var it = this.iterator(); it.hasNext();) {
106264               var de = it.next();
106265               var label = de.getLabel();
106266               label.setAllLocationsIfNull(0, nodeLabel.getLocation(0));
106267               label.setAllLocationsIfNull(1, nodeLabel.getLocation(1));
106268             }
106269           };
106270           DirectedEdgeStar.prototype.linkAllDirectedEdges = function linkAllDirectedEdges () {
106271             var this$1 = this;
106272
106273             this.getEdges();
106274             var prevOut = null;
106275             var firstIn = null;
106276             for (var i = this._edgeList.size() - 1; i >= 0; i--) {
106277               var nextOut = this$1._edgeList.get(i);
106278               var nextIn = nextOut.getSym();
106279               if (firstIn === null) { firstIn = nextIn; }
106280               if (prevOut !== null) { nextIn.setNext(prevOut); }
106281               prevOut = nextOut;
106282             }
106283             firstIn.setNext(prevOut);
106284           };
106285           DirectedEdgeStar.prototype.computeDepths = function computeDepths () {
106286             var this$1 = this;
106287
106288             if (arguments.length === 1) {
106289               var de = arguments[0];
106290               var edgeIndex = this.findIndex(de);
106291               // const label = de.getLabel()
106292               var startDepth = de.getDepth(Position.LEFT);
106293               var targetLastDepth = de.getDepth(Position.RIGHT);
106294               var nextDepth = this.computeDepths(edgeIndex + 1, this._edgeList.size(), startDepth);
106295               var lastDepth = this.computeDepths(0, edgeIndex, nextDepth);
106296               if (lastDepth !== targetLastDepth) { throw new TopologyException('depth mismatch at ' + de.getCoordinate()) }
106297             } else if (arguments.length === 3) {
106298               var startIndex = arguments[0];
106299               var endIndex = arguments[1];
106300               var startDepth$1 = arguments[2];
106301               var currDepth = startDepth$1;
106302               for (var i = startIndex; i < endIndex; i++) {
106303                 var nextDe = this$1._edgeList.get(i);
106304                 // const label = nextDe.getLabel()
106305                 nextDe.setEdgeDepths(Position.RIGHT, currDepth);
106306                 currDepth = nextDe.getDepth(Position.LEFT);
106307               }
106308               return currDepth
106309             }
106310           };
106311           DirectedEdgeStar.prototype.mergeSymLabels = function mergeSymLabels () {
106312             for (var it = this.iterator(); it.hasNext();) {
106313               var de = it.next();
106314               var label = de.getLabel();
106315               label.merge(de.getSym().getLabel());
106316             }
106317           };
106318           DirectedEdgeStar.prototype.linkMinimalDirectedEdges = function linkMinimalDirectedEdges (er) {
106319             var this$1 = this;
106320
106321             var firstOut = null;
106322             var incoming = null;
106323             var state = this._SCANNING_FOR_INCOMING;
106324             for (var i = this._resultAreaEdgeList.size() - 1; i >= 0; i--) {
106325               var nextOut = this$1._resultAreaEdgeList.get(i);
106326               var nextIn = nextOut.getSym();
106327               if (firstOut === null && nextOut.getEdgeRing() === er) { firstOut = nextOut; }
106328               switch (state) {
106329                 case this$1._SCANNING_FOR_INCOMING:
106330                   if (nextIn.getEdgeRing() !== er) { continue }
106331                   incoming = nextIn;
106332                   state = this$1._LINKING_TO_OUTGOING;
106333                   break
106334                 case this$1._LINKING_TO_OUTGOING:
106335                   if (nextOut.getEdgeRing() !== er) { continue }
106336                   incoming.setNextMin(nextOut);
106337                   state = this$1._SCANNING_FOR_INCOMING;
106338                   break
106339               }
106340             }
106341             if (state === this._LINKING_TO_OUTGOING) {
106342               Assert.isTrue(firstOut !== null, 'found null for first outgoing dirEdge');
106343               Assert.isTrue(firstOut.getEdgeRing() === er, 'unable to link last incoming dirEdge');
106344               incoming.setNextMin(firstOut);
106345             }
106346           };
106347           DirectedEdgeStar.prototype.getOutgoingDegree = function getOutgoingDegree () {
106348             if (arguments.length === 0) {
106349               var degree = 0;
106350               for (var it = this.iterator(); it.hasNext();) {
106351                 var de = it.next();
106352                 if (de.isInResult()) { degree++; }
106353               }
106354               return degree
106355             } else if (arguments.length === 1) {
106356               var er = arguments[0];
106357               var degree$1 = 0;
106358               for (var it$1 = this.iterator(); it$1.hasNext();) {
106359                 var de$1 = it$1.next();
106360                 if (de$1.getEdgeRing() === er) { degree$1++; }
106361               }
106362               return degree$1
106363             }
106364           };
106365           DirectedEdgeStar.prototype.getLabel = function getLabel () {
106366             return this._label
106367           };
106368           DirectedEdgeStar.prototype.findCoveredLineEdges = function findCoveredLineEdges () {
106369             var startLoc = Location.NONE;
106370             for (var it = this.iterator(); it.hasNext();) {
106371               var nextOut = it.next();
106372               var nextIn = nextOut.getSym();
106373               if (!nextOut.isLineEdge()) {
106374                 if (nextOut.isInResult()) {
106375                   startLoc = Location.INTERIOR;
106376                   break
106377                 }
106378                 if (nextIn.isInResult()) {
106379                   startLoc = Location.EXTERIOR;
106380                   break
106381                 }
106382               }
106383             }
106384             if (startLoc === Location.NONE) { return null }
106385             var currLoc = startLoc;
106386             for (var it$1 = this.iterator(); it$1.hasNext();) {
106387               var nextOut$1 = it$1.next();
106388               var nextIn$1 = nextOut$1.getSym();
106389               if (nextOut$1.isLineEdge()) {
106390                 nextOut$1.getEdge().setCovered(currLoc === Location.INTERIOR);
106391               } else {
106392                 if (nextOut$1.isInResult()) { currLoc = Location.EXTERIOR; }
106393                 if (nextIn$1.isInResult()) { currLoc = Location.INTERIOR; }
106394               }
106395             }
106396           };
106397           DirectedEdgeStar.prototype.computeLabelling = function computeLabelling (geom) {
106398             var this$1 = this;
106399
106400             EdgeEndStar$$1.prototype.computeLabelling.call(this, geom);
106401             this._label = new Label(Location.NONE);
106402             for (var it = this.iterator(); it.hasNext();) {
106403               var ee = it.next();
106404               var e = ee.getEdge();
106405               var eLabel = e.getLabel();
106406               for (var i = 0; i < 2; i++) {
106407                 var eLoc = eLabel.getLocation(i);
106408                 if (eLoc === Location.INTERIOR || eLoc === Location.BOUNDARY) { this$1._label.setLocation(i, Location.INTERIOR); }
106409               }
106410             }
106411           };
106412           DirectedEdgeStar.prototype.interfaces_ = function interfaces_ () {
106413             return []
106414           };
106415           DirectedEdgeStar.prototype.getClass = function getClass () {
106416             return DirectedEdgeStar
106417           };
106418
106419           return DirectedEdgeStar;
106420         }(EdgeEndStar));
106421
106422         var OverlayNodeFactory = (function (NodeFactory$$1) {
106423           function OverlayNodeFactory () {
106424             NodeFactory$$1.apply(this, arguments);
106425           }
106426
106427           if ( NodeFactory$$1 ) { OverlayNodeFactory.__proto__ = NodeFactory$$1; }
106428           OverlayNodeFactory.prototype = Object.create( NodeFactory$$1 && NodeFactory$$1.prototype );
106429           OverlayNodeFactory.prototype.constructor = OverlayNodeFactory;
106430
106431           OverlayNodeFactory.prototype.createNode = function createNode (coord) {
106432             return new Node$1(coord, new DirectedEdgeStar())
106433           };
106434           OverlayNodeFactory.prototype.interfaces_ = function interfaces_ () {
106435             return []
106436           };
106437           OverlayNodeFactory.prototype.getClass = function getClass () {
106438             return OverlayNodeFactory
106439           };
106440
106441           return OverlayNodeFactory;
106442         }(NodeFactory));
106443
106444         var OrientedCoordinateArray = function OrientedCoordinateArray () {
106445           this._pts = null;
106446           this._orientation = null;
106447           var pts = arguments[0];
106448           this._pts = pts;
106449           this._orientation = OrientedCoordinateArray.orientation(pts);
106450         };
106451         OrientedCoordinateArray.prototype.compareTo = function compareTo (o1) {
106452           var oca = o1;
106453           var comp = OrientedCoordinateArray.compareOriented(this._pts, this._orientation, oca._pts, oca._orientation);
106454           return comp
106455         };
106456         OrientedCoordinateArray.prototype.interfaces_ = function interfaces_ () {
106457           return [Comparable]
106458         };
106459         OrientedCoordinateArray.prototype.getClass = function getClass () {
106460           return OrientedCoordinateArray
106461         };
106462         OrientedCoordinateArray.orientation = function orientation (pts) {
106463           return CoordinateArrays.increasingDirection(pts) === 1
106464         };
106465         OrientedCoordinateArray.compareOriented = function compareOriented (pts1, orientation1, pts2, orientation2) {
106466           var dir1 = orientation1 ? 1 : -1;
106467           var dir2 = orientation2 ? 1 : -1;
106468           var limit1 = orientation1 ? pts1.length : -1;
106469           var limit2 = orientation2 ? pts2.length : -1;
106470           var i1 = orientation1 ? 0 : pts1.length - 1;
106471           var i2 = orientation2 ? 0 : pts2.length - 1;
106472           // const comp = 0
106473           while (true) {
106474             var compPt = pts1[i1].compareTo(pts2[i2]);
106475             if (compPt !== 0) { return compPt }
106476             i1 += dir1;
106477             i2 += dir2;
106478             var done1 = i1 === limit1;
106479             var done2 = i2 === limit2;
106480             if (done1 && !done2) { return -1 }
106481             if (!done1 && done2) { return 1 }
106482             if (done1 && done2) { return 0 }
106483           }
106484         };
106485
106486         var EdgeList = function EdgeList () {
106487           this._edges = new ArrayList();
106488           this._ocaMap = new TreeMap();
106489         };
106490         EdgeList.prototype.print = function print (out) {
106491             var this$1 = this;
106492
106493           out.print('MULTILINESTRING ( ');
106494           for (var j = 0; j < this._edges.size(); j++) {
106495             var e = this$1._edges.get(j);
106496             if (j > 0) { out.print(','); }
106497             out.print('(');
106498             var pts = e.getCoordinates();
106499             for (var i = 0; i < pts.length; i++) {
106500               if (i > 0) { out.print(','); }
106501               out.print(pts[i].x + ' ' + pts[i].y);
106502             }
106503             out.println(')');
106504           }
106505           out.print(')  ');
106506         };
106507         EdgeList.prototype.addAll = function addAll (edgeColl) {
106508             var this$1 = this;
106509
106510           for (var i = edgeColl.iterator(); i.hasNext();) {
106511             this$1.add(i.next());
106512           }
106513         };
106514         EdgeList.prototype.findEdgeIndex = function findEdgeIndex (e) {
106515             var this$1 = this;
106516
106517           for (var i = 0; i < this._edges.size(); i++) {
106518             if (this$1._edges.get(i).equals(e)) { return i }
106519           }
106520           return -1
106521         };
106522         EdgeList.prototype.iterator = function iterator () {
106523           return this._edges.iterator()
106524         };
106525         EdgeList.prototype.getEdges = function getEdges () {
106526           return this._edges
106527         };
106528         EdgeList.prototype.get = function get (i) {
106529           return this._edges.get(i)
106530         };
106531         EdgeList.prototype.findEqualEdge = function findEqualEdge (e) {
106532           var oca = new OrientedCoordinateArray(e.getCoordinates());
106533           var matchEdge = this._ocaMap.get(oca);
106534           return matchEdge
106535         };
106536         EdgeList.prototype.add = function add (e) {
106537           this._edges.add(e);
106538           var oca = new OrientedCoordinateArray(e.getCoordinates());
106539           this._ocaMap.put(oca, e);
106540         };
106541         EdgeList.prototype.interfaces_ = function interfaces_ () {
106542           return []
106543         };
106544         EdgeList.prototype.getClass = function getClass () {
106545           return EdgeList
106546         };
106547
106548         var SegmentIntersector = function SegmentIntersector () {};
106549
106550         SegmentIntersector.prototype.processIntersections = function processIntersections (e0, segIndex0, e1, segIndex1) {};
106551         SegmentIntersector.prototype.isDone = function isDone () {};
106552         SegmentIntersector.prototype.interfaces_ = function interfaces_ () {
106553           return []
106554         };
106555         SegmentIntersector.prototype.getClass = function getClass () {
106556           return SegmentIntersector
106557         };
106558
106559         var IntersectionAdder = function IntersectionAdder () {
106560           this._hasIntersection = false;
106561           this._hasProper = false;
106562           this._hasProperInterior = false;
106563           this._hasInterior = false;
106564           this._properIntersectionPoint = null;
106565           this._li = null;
106566           this._isSelfIntersection = null;
106567           this.numIntersections = 0;
106568           this.numInteriorIntersections = 0;
106569           this.numProperIntersections = 0;
106570           this.numTests = 0;
106571           var li = arguments[0];
106572           this._li = li;
106573         };
106574         IntersectionAdder.prototype.isTrivialIntersection = function isTrivialIntersection (e0, segIndex0, e1, segIndex1) {
106575           if (e0 === e1) {
106576             if (this._li.getIntersectionNum() === 1) {
106577               if (IntersectionAdder.isAdjacentSegments(segIndex0, segIndex1)) { return true }
106578               if (e0.isClosed()) {
106579                 var maxSegIndex = e0.size() - 1;
106580                 if ((segIndex0 === 0 && segIndex1 === maxSegIndex) ||
106581                     (segIndex1 === 0 && segIndex0 === maxSegIndex)) {
106582                   return true
106583                 }
106584               }
106585             }
106586           }
106587           return false
106588         };
106589         IntersectionAdder.prototype.getProperIntersectionPoint = function getProperIntersectionPoint () {
106590           return this._properIntersectionPoint
106591         };
106592         IntersectionAdder.prototype.hasProperInteriorIntersection = function hasProperInteriorIntersection () {
106593           return this._hasProperInterior
106594         };
106595         IntersectionAdder.prototype.getLineIntersector = function getLineIntersector () {
106596           return this._li
106597         };
106598         IntersectionAdder.prototype.hasProperIntersection = function hasProperIntersection () {
106599           return this._hasProper
106600         };
106601         IntersectionAdder.prototype.processIntersections = function processIntersections (e0, segIndex0, e1, segIndex1) {
106602           if (e0 === e1 && segIndex0 === segIndex1) { return null }
106603           this.numTests++;
106604           var p00 = e0.getCoordinates()[segIndex0];
106605           var p01 = e0.getCoordinates()[segIndex0 + 1];
106606           var p10 = e1.getCoordinates()[segIndex1];
106607           var p11 = e1.getCoordinates()[segIndex1 + 1];
106608           this._li.computeIntersection(p00, p01, p10, p11);
106609           if (this._li.hasIntersection()) {
106610             this.numIntersections++;
106611             if (this._li.isInteriorIntersection()) {
106612               this.numInteriorIntersections++;
106613               this._hasInterior = true;
106614             }
106615             if (!this.isTrivialIntersection(e0, segIndex0, e1, segIndex1)) {
106616               this._hasIntersection = true;
106617               e0.addIntersections(this._li, segIndex0, 0);
106618               e1.addIntersections(this._li, segIndex1, 1);
106619               if (this._li.isProper()) {
106620                 this.numProperIntersections++;
106621                 this._hasProper = true;
106622                 this._hasProperInterior = true;
106623               }
106624             }
106625           }
106626         };
106627         IntersectionAdder.prototype.hasIntersection = function hasIntersection () {
106628           return this._hasIntersection
106629         };
106630         IntersectionAdder.prototype.isDone = function isDone () {
106631           return false
106632         };
106633         IntersectionAdder.prototype.hasInteriorIntersection = function hasInteriorIntersection () {
106634           return this._hasInterior
106635         };
106636         IntersectionAdder.prototype.interfaces_ = function interfaces_ () {
106637           return [SegmentIntersector]
106638         };
106639         IntersectionAdder.prototype.getClass = function getClass () {
106640           return IntersectionAdder
106641         };
106642         IntersectionAdder.isAdjacentSegments = function isAdjacentSegments (i1, i2) {
106643           return Math.abs(i1 - i2) === 1
106644         };
106645
106646         var EdgeIntersection = function EdgeIntersection () {
106647           this.coord = null;
106648           this.segmentIndex = null;
106649           this.dist = null;
106650           var coord = arguments[0];
106651           var segmentIndex = arguments[1];
106652           var dist = arguments[2];
106653           this.coord = new Coordinate(coord);
106654           this.segmentIndex = segmentIndex;
106655           this.dist = dist;
106656         };
106657         EdgeIntersection.prototype.getSegmentIndex = function getSegmentIndex () {
106658           return this.segmentIndex
106659         };
106660         EdgeIntersection.prototype.getCoordinate = function getCoordinate () {
106661           return this.coord
106662         };
106663         EdgeIntersection.prototype.print = function print (out) {
106664           out.print(this.coord);
106665           out.print(' seg # = ' + this.segmentIndex);
106666           out.println(' dist = ' + this.dist);
106667         };
106668         EdgeIntersection.prototype.compareTo = function compareTo (obj) {
106669           var other = obj;
106670           return this.compare(other.segmentIndex, other.dist)
106671         };
106672         EdgeIntersection.prototype.isEndPoint = function isEndPoint (maxSegmentIndex) {
106673           if (this.segmentIndex === 0 && this.dist === 0.0) { return true }
106674           if (this.segmentIndex === maxSegmentIndex) { return true }
106675           return false
106676         };
106677         EdgeIntersection.prototype.toString = function toString () {
106678           return this.coord + ' seg # = ' + this.segmentIndex + ' dist = ' + this.dist
106679         };
106680         EdgeIntersection.prototype.getDistance = function getDistance () {
106681           return this.dist
106682         };
106683         EdgeIntersection.prototype.compare = function compare (segmentIndex, dist) {
106684           if (this.segmentIndex < segmentIndex) { return -1 }
106685           if (this.segmentIndex > segmentIndex) { return 1 }
106686           if (this.dist < dist) { return -1 }
106687           if (this.dist > dist) { return 1 }
106688           return 0
106689         };
106690         EdgeIntersection.prototype.interfaces_ = function interfaces_ () {
106691           return [Comparable]
106692         };
106693         EdgeIntersection.prototype.getClass = function getClass () {
106694           return EdgeIntersection
106695         };
106696
106697         var EdgeIntersectionList = function EdgeIntersectionList () {
106698           this._nodeMap = new TreeMap();
106699           this.edge = null;
106700           var edge = arguments[0];
106701           this.edge = edge;
106702         };
106703         EdgeIntersectionList.prototype.print = function print (out) {
106704           out.println('Intersections:');
106705           for (var it = this.iterator(); it.hasNext();) {
106706             var ei = it.next();
106707             ei.print(out);
106708           }
106709         };
106710         EdgeIntersectionList.prototype.iterator = function iterator () {
106711           return this._nodeMap.values().iterator()
106712         };
106713         EdgeIntersectionList.prototype.addSplitEdges = function addSplitEdges (edgeList) {
106714             var this$1 = this;
106715
106716           this.addEndpoints();
106717           var it = this.iterator();
106718           var eiPrev = it.next();
106719           while (it.hasNext()) {
106720             var ei = it.next();
106721             var newEdge = this$1.createSplitEdge(eiPrev, ei);
106722             edgeList.add(newEdge);
106723             eiPrev = ei;
106724           }
106725         };
106726         EdgeIntersectionList.prototype.addEndpoints = function addEndpoints () {
106727           var maxSegIndex = this.edge.pts.length - 1;
106728           this.add(this.edge.pts[0], 0, 0.0);
106729           this.add(this.edge.pts[maxSegIndex], maxSegIndex, 0.0);
106730         };
106731         EdgeIntersectionList.prototype.createSplitEdge = function createSplitEdge (ei0, ei1) {
106732             var this$1 = this;
106733
106734           var npts = ei1.segmentIndex - ei0.segmentIndex + 2;
106735           var lastSegStartPt = this.edge.pts[ei1.segmentIndex];
106736           var useIntPt1 = ei1.dist > 0.0 || !ei1.coord.equals2D(lastSegStartPt);
106737           if (!useIntPt1) {
106738             npts--;
106739           }
106740           var pts = new Array(npts).fill(null);
106741           var ipt = 0;
106742           pts[ipt++] = new Coordinate(ei0.coord);
106743           for (var i = ei0.segmentIndex + 1; i <= ei1.segmentIndex; i++) {
106744             pts[ipt++] = this$1.edge.pts[i];
106745           }
106746           if (useIntPt1) { pts[ipt] = ei1.coord; }
106747           return new Edge(pts, new Label(this.edge._label))
106748         };
106749         EdgeIntersectionList.prototype.add = function add (intPt, segmentIndex, dist) {
106750           var eiNew = new EdgeIntersection(intPt, segmentIndex, dist);
106751           var ei = this._nodeMap.get(eiNew);
106752           if (ei !== null) {
106753             return ei
106754           }
106755           this._nodeMap.put(eiNew, eiNew);
106756           return eiNew
106757         };
106758         EdgeIntersectionList.prototype.isIntersection = function isIntersection (pt) {
106759           for (var it = this.iterator(); it.hasNext();) {
106760             var ei = it.next();
106761             if (ei.coord.equals(pt)) { return true }
106762           }
106763           return false
106764         };
106765         EdgeIntersectionList.prototype.interfaces_ = function interfaces_ () {
106766           return []
106767         };
106768         EdgeIntersectionList.prototype.getClass = function getClass () {
106769           return EdgeIntersectionList
106770         };
106771
106772         var MonotoneChainIndexer = function MonotoneChainIndexer () {};
106773
106774         MonotoneChainIndexer.prototype.getChainStartIndices = function getChainStartIndices (pts) {
106775             var this$1 = this;
106776
106777           var start = 0;
106778           var startIndexList = new ArrayList();
106779           startIndexList.add(new Integer(start));
106780           do {
106781             var last = this$1.findChainEnd(pts, start);
106782             startIndexList.add(new Integer(last));
106783             start = last;
106784           } while (start < pts.length - 1)
106785           var startIndex = MonotoneChainIndexer.toIntArray(startIndexList);
106786           return startIndex
106787         };
106788         MonotoneChainIndexer.prototype.findChainEnd = function findChainEnd (pts, start) {
106789           var chainQuad = Quadrant.quadrant(pts[start], pts[start + 1]);
106790           var last = start + 1;
106791           while (last < pts.length) {
106792             var quad = Quadrant.quadrant(pts[last - 1], pts[last]);
106793             if (quad !== chainQuad) { break }
106794             last++;
106795           }
106796           return last - 1
106797         };
106798         MonotoneChainIndexer.prototype.interfaces_ = function interfaces_ () {
106799           return []
106800         };
106801         MonotoneChainIndexer.prototype.getClass = function getClass () {
106802           return MonotoneChainIndexer
106803         };
106804         MonotoneChainIndexer.toIntArray = function toIntArray (list) {
106805           var array = new Array(list.size()).fill(null);
106806           for (var i = 0; i < array.length; i++) {
106807             array[i] = list.get(i).intValue();
106808           }
106809           return array
106810         };
106811
106812         var MonotoneChainEdge = function MonotoneChainEdge () {
106813           this.e = null;
106814           this.pts = null;
106815           this.startIndex = null;
106816           this.env1 = new Envelope();
106817           this.env2 = new Envelope();
106818           var e = arguments[0];
106819           this.e = e;
106820           this.pts = e.getCoordinates();
106821           var mcb = new MonotoneChainIndexer();
106822           this.startIndex = mcb.getChainStartIndices(this.pts);
106823         };
106824         MonotoneChainEdge.prototype.getCoordinates = function getCoordinates () {
106825           return this.pts
106826         };
106827         MonotoneChainEdge.prototype.getMaxX = function getMaxX (chainIndex) {
106828           var x1 = this.pts[this.startIndex[chainIndex]].x;
106829           var x2 = this.pts[this.startIndex[chainIndex + 1]].x;
106830           return x1 > x2 ? x1 : x2
106831         };
106832         MonotoneChainEdge.prototype.getMinX = function getMinX (chainIndex) {
106833           var x1 = this.pts[this.startIndex[chainIndex]].x;
106834           var x2 = this.pts[this.startIndex[chainIndex + 1]].x;
106835           return x1 < x2 ? x1 : x2
106836         };
106837         MonotoneChainEdge.prototype.computeIntersectsForChain = function computeIntersectsForChain () {
106838           if (arguments.length === 4) {
106839             var chainIndex0 = arguments[0];
106840             var mce = arguments[1];
106841             var chainIndex1 = arguments[2];
106842             var si = arguments[3];
106843             this.computeIntersectsForChain(this.startIndex[chainIndex0], this.startIndex[chainIndex0 + 1], mce, mce.startIndex[chainIndex1], mce.startIndex[chainIndex1 + 1], si);
106844           } else if (arguments.length === 6) {
106845             var start0 = arguments[0];
106846             var end0 = arguments[1];
106847             var mce$1 = arguments[2];
106848             var start1 = arguments[3];
106849             var end1 = arguments[4];
106850             var ei = arguments[5];
106851             var p00 = this.pts[start0];
106852             var p01 = this.pts[end0];
106853             var p10 = mce$1.pts[start1];
106854             var p11 = mce$1.pts[end1];
106855             if (end0 - start0 === 1 && end1 - start1 === 1) {
106856               ei.addIntersections(this.e, start0, mce$1.e, start1);
106857               return null
106858             }
106859             this.env1.init(p00, p01);
106860             this.env2.init(p10, p11);
106861             if (!this.env1.intersects(this.env2)) { return null }
106862             var mid0 = Math.trunc((start0 + end0) / 2);
106863             var mid1 = Math.trunc((start1 + end1) / 2);
106864             if (start0 < mid0) {
106865               if (start1 < mid1) { this.computeIntersectsForChain(start0, mid0, mce$1, start1, mid1, ei); }
106866               if (mid1 < end1) { this.computeIntersectsForChain(start0, mid0, mce$1, mid1, end1, ei); }
106867             }
106868             if (mid0 < end0) {
106869               if (start1 < mid1) { this.computeIntersectsForChain(mid0, end0, mce$1, start1, mid1, ei); }
106870               if (mid1 < end1) { this.computeIntersectsForChain(mid0, end0, mce$1, mid1, end1, ei); }
106871             }
106872           }
106873         };
106874         MonotoneChainEdge.prototype.getStartIndexes = function getStartIndexes () {
106875           return this.startIndex
106876         };
106877         MonotoneChainEdge.prototype.computeIntersects = function computeIntersects (mce, si) {
106878             var this$1 = this;
106879
106880           for (var i = 0; i < this.startIndex.length - 1; i++) {
106881             for (var j = 0; j < mce.startIndex.length - 1; j++) {
106882               this$1.computeIntersectsForChain(i, mce, j, si);
106883             }
106884           }
106885         };
106886         MonotoneChainEdge.prototype.interfaces_ = function interfaces_ () {
106887           return []
106888         };
106889         MonotoneChainEdge.prototype.getClass = function getClass () {
106890           return MonotoneChainEdge
106891         };
106892
106893         var Depth = function Depth () {
106894           var this$1 = this;
106895
106896           this._depth = Array(2).fill().map(function () { return Array(3); });
106897           for (var i = 0; i < 2; i++) {
106898             for (var j = 0; j < 3; j++) {
106899               this$1._depth[i][j] = Depth.NULL_VALUE;
106900             }
106901           }
106902         };
106903
106904         var staticAccessors$31 = { NULL_VALUE: { configurable: true } };
106905         Depth.prototype.getDepth = function getDepth (geomIndex, posIndex) {
106906           return this._depth[geomIndex][posIndex]
106907         };
106908         Depth.prototype.setDepth = function setDepth (geomIndex, posIndex, depthValue) {
106909           this._depth[geomIndex][posIndex] = depthValue;
106910         };
106911         Depth.prototype.isNull = function isNull () {
106912             var this$1 = this;
106913
106914           if (arguments.length === 0) {
106915             for (var i = 0; i < 2; i++) {
106916               for (var j = 0; j < 3; j++) {
106917                 if (this$1._depth[i][j] !== Depth.NULL_VALUE) { return false }
106918               }
106919             }
106920             return true
106921           } else if (arguments.length === 1) {
106922             var geomIndex = arguments[0];
106923             return this._depth[geomIndex][1] === Depth.NULL_VALUE
106924           } else if (arguments.length === 2) {
106925             var geomIndex$1 = arguments[0];
106926             var posIndex = arguments[1];
106927             return this._depth[geomIndex$1][posIndex] === Depth.NULL_VALUE
106928           }
106929         };
106930         Depth.prototype.normalize = function normalize () {
106931             var this$1 = this;
106932
106933           for (var i = 0; i < 2; i++) {
106934             if (!this$1.isNull(i)) {
106935               var minDepth = this$1._depth[i][1];
106936               if (this$1._depth[i][2] < minDepth) { minDepth = this$1._depth[i][2]; }
106937               if (minDepth < 0) { minDepth = 0; }
106938               for (var j = 1; j < 3; j++) {
106939                 var newValue = 0;
106940                 if (this$1._depth[i][j] > minDepth) { newValue = 1; }
106941                 this$1._depth[i][j] = newValue;
106942               }
106943             }
106944           }
106945         };
106946         Depth.prototype.getDelta = function getDelta (geomIndex) {
106947           return this._depth[geomIndex][Position.RIGHT] - this._depth[geomIndex][Position.LEFT]
106948         };
106949         Depth.prototype.getLocation = function getLocation (geomIndex, posIndex) {
106950           if (this._depth[geomIndex][posIndex] <= 0) { return Location.EXTERIOR }
106951           return Location.INTERIOR
106952         };
106953         Depth.prototype.toString = function toString () {
106954           return 'A: ' + this._depth[0][1] + ',' + this._depth[0][2] + ' B: ' + this._depth[1][1] + ',' + this._depth[1][2]
106955         };
106956         Depth.prototype.add = function add () {
106957             var this$1 = this;
106958
106959           if (arguments.length === 1) {
106960             var lbl = arguments[0];
106961             for (var i = 0; i < 2; i++) {
106962               for (var j = 1; j < 3; j++) {
106963                 var loc = lbl.getLocation(i, j);
106964                 if (loc === Location.EXTERIOR || loc === Location.INTERIOR) {
106965                   if (this$1.isNull(i, j)) {
106966                     this$1._depth[i][j] = Depth.depthAtLocation(loc);
106967                   } else { this$1._depth[i][j] += Depth.depthAtLocation(loc); }
106968                 }
106969               }
106970             }
106971           } else if (arguments.length === 3) {
106972             var geomIndex = arguments[0];
106973             var posIndex = arguments[1];
106974             var location = arguments[2];
106975             if (location === Location.INTERIOR) { this._depth[geomIndex][posIndex]++; }
106976           }
106977         };
106978         Depth.prototype.interfaces_ = function interfaces_ () {
106979           return []
106980         };
106981         Depth.prototype.getClass = function getClass () {
106982           return Depth
106983         };
106984         Depth.depthAtLocation = function depthAtLocation (location) {
106985           if (location === Location.EXTERIOR) { return 0 }
106986           if (location === Location.INTERIOR) { return 1 }
106987           return Depth.NULL_VALUE
106988         };
106989         staticAccessors$31.NULL_VALUE.get = function () { return -1 };
106990
106991         Object.defineProperties( Depth, staticAccessors$31 );
106992
106993         var Edge = (function (GraphComponent$$1) {
106994           function Edge () {
106995             GraphComponent$$1.call(this);
106996             this.pts = null;
106997             this._env = null;
106998             this.eiList = new EdgeIntersectionList(this);
106999             this._name = null;
107000             this._mce = null;
107001             this._isIsolated = true;
107002             this._depth = new Depth();
107003             this._depthDelta = 0;
107004             if (arguments.length === 1) {
107005               var pts = arguments[0];
107006               Edge.call(this, pts, null);
107007             } else if (arguments.length === 2) {
107008               var pts$1 = arguments[0];
107009               var label = arguments[1];
107010               this.pts = pts$1;
107011               this._label = label;
107012             }
107013           }
107014
107015           if ( GraphComponent$$1 ) { Edge.__proto__ = GraphComponent$$1; }
107016           Edge.prototype = Object.create( GraphComponent$$1 && GraphComponent$$1.prototype );
107017           Edge.prototype.constructor = Edge;
107018           Edge.prototype.getDepth = function getDepth () {
107019             return this._depth
107020           };
107021           Edge.prototype.getCollapsedEdge = function getCollapsedEdge () {
107022             var newPts = new Array(2).fill(null);
107023             newPts[0] = this.pts[0];
107024             newPts[1] = this.pts[1];
107025             var newe = new Edge(newPts, Label.toLineLabel(this._label));
107026             return newe
107027           };
107028           Edge.prototype.isIsolated = function isIsolated () {
107029             return this._isIsolated
107030           };
107031           Edge.prototype.getCoordinates = function getCoordinates () {
107032             return this.pts
107033           };
107034           Edge.prototype.setIsolated = function setIsolated (isIsolated) {
107035             this._isIsolated = isIsolated;
107036           };
107037           Edge.prototype.setName = function setName (name) {
107038             this._name = name;
107039           };
107040           Edge.prototype.equals = function equals (o) {
107041             var this$1 = this;
107042
107043             if (!(o instanceof Edge)) { return false }
107044             var e = o;
107045             if (this.pts.length !== e.pts.length) { return false }
107046             var isEqualForward = true;
107047             var isEqualReverse = true;
107048             var iRev = this.pts.length;
107049             for (var i = 0; i < this.pts.length; i++) {
107050               if (!this$1.pts[i].equals2D(e.pts[i])) {
107051                 isEqualForward = false;
107052               }
107053               if (!this$1.pts[i].equals2D(e.pts[--iRev])) {
107054                 isEqualReverse = false;
107055               }
107056               if (!isEqualForward && !isEqualReverse) { return false }
107057             }
107058             return true
107059           };
107060           Edge.prototype.getCoordinate = function getCoordinate () {
107061             if (arguments.length === 0) {
107062               if (this.pts.length > 0) { return this.pts[0] }
107063               return null
107064             } else if (arguments.length === 1) {
107065               var i = arguments[0];
107066               return this.pts[i]
107067             }
107068           };
107069           Edge.prototype.print = function print (out) {
107070             var this$1 = this;
107071
107072             out.print('edge ' + this._name + ': ');
107073             out.print('LINESTRING (');
107074             for (var i = 0; i < this.pts.length; i++) {
107075               if (i > 0) { out.print(','); }
107076               out.print(this$1.pts[i].x + ' ' + this$1.pts[i].y);
107077             }
107078             out.print(')  ' + this._label + ' ' + this._depthDelta);
107079           };
107080           Edge.prototype.computeIM = function computeIM (im) {
107081             Edge.updateIM(this._label, im);
107082           };
107083           Edge.prototype.isCollapsed = function isCollapsed () {
107084             if (!this._label.isArea()) { return false }
107085             if (this.pts.length !== 3) { return false }
107086             if (this.pts[0].equals(this.pts[2])) { return true }
107087             return false
107088           };
107089           Edge.prototype.isClosed = function isClosed () {
107090             return this.pts[0].equals(this.pts[this.pts.length - 1])
107091           };
107092           Edge.prototype.getMaximumSegmentIndex = function getMaximumSegmentIndex () {
107093             return this.pts.length - 1
107094           };
107095           Edge.prototype.getDepthDelta = function getDepthDelta () {
107096             return this._depthDelta
107097           };
107098           Edge.prototype.getNumPoints = function getNumPoints () {
107099             return this.pts.length
107100           };
107101           Edge.prototype.printReverse = function printReverse (out) {
107102             var this$1 = this;
107103
107104             out.print('edge ' + this._name + ': ');
107105             for (var i = this.pts.length - 1; i >= 0; i--) {
107106               out.print(this$1.pts[i] + ' ');
107107             }
107108             out.println('');
107109           };
107110           Edge.prototype.getMonotoneChainEdge = function getMonotoneChainEdge () {
107111             if (this._mce === null) { this._mce = new MonotoneChainEdge(this); }
107112             return this._mce
107113           };
107114           Edge.prototype.getEnvelope = function getEnvelope () {
107115             var this$1 = this;
107116
107117             if (this._env === null) {
107118               this._env = new Envelope();
107119               for (var i = 0; i < this.pts.length; i++) {
107120                 this$1._env.expandToInclude(this$1.pts[i]);
107121               }
107122             }
107123             return this._env
107124           };
107125           Edge.prototype.addIntersection = function addIntersection (li, segmentIndex, geomIndex, intIndex) {
107126             var intPt = new Coordinate(li.getIntersection(intIndex));
107127             var normalizedSegmentIndex = segmentIndex;
107128             var dist = li.getEdgeDistance(geomIndex, intIndex);
107129             var nextSegIndex = normalizedSegmentIndex + 1;
107130             if (nextSegIndex < this.pts.length) {
107131               var nextPt = this.pts[nextSegIndex];
107132               if (intPt.equals2D(nextPt)) {
107133                 normalizedSegmentIndex = nextSegIndex;
107134                 dist = 0.0;
107135               }
107136             }
107137             this.eiList.add(intPt, normalizedSegmentIndex, dist);
107138           };
107139           Edge.prototype.toString = function toString () {
107140             var this$1 = this;
107141
107142             var buf = new StringBuffer();
107143             buf.append('edge ' + this._name + ': ');
107144             buf.append('LINESTRING (');
107145             for (var i = 0; i < this.pts.length; i++) {
107146               if (i > 0) { buf.append(','); }
107147               buf.append(this$1.pts[i].x + ' ' + this$1.pts[i].y);
107148             }
107149             buf.append(')  ' + this._label + ' ' + this._depthDelta);
107150             return buf.toString()
107151           };
107152           Edge.prototype.isPointwiseEqual = function isPointwiseEqual (e) {
107153             var this$1 = this;
107154
107155             if (this.pts.length !== e.pts.length) { return false }
107156             for (var i = 0; i < this.pts.length; i++) {
107157               if (!this$1.pts[i].equals2D(e.pts[i])) {
107158                 return false
107159               }
107160             }
107161             return true
107162           };
107163           Edge.prototype.setDepthDelta = function setDepthDelta (depthDelta) {
107164             this._depthDelta = depthDelta;
107165           };
107166           Edge.prototype.getEdgeIntersectionList = function getEdgeIntersectionList () {
107167             return this.eiList
107168           };
107169           Edge.prototype.addIntersections = function addIntersections (li, segmentIndex, geomIndex) {
107170             var this$1 = this;
107171
107172             for (var i = 0; i < li.getIntersectionNum(); i++) {
107173               this$1.addIntersection(li, segmentIndex, geomIndex, i);
107174             }
107175           };
107176           Edge.prototype.interfaces_ = function interfaces_ () {
107177             return []
107178           };
107179           Edge.prototype.getClass = function getClass () {
107180             return Edge
107181           };
107182           Edge.updateIM = function updateIM () {
107183             if (arguments.length === 2) {
107184               var label = arguments[0];
107185               var im = arguments[1];
107186               im.setAtLeastIfValid(label.getLocation(0, Position.ON), label.getLocation(1, Position.ON), 1);
107187               if (label.isArea()) {
107188                 im.setAtLeastIfValid(label.getLocation(0, Position.LEFT), label.getLocation(1, Position.LEFT), 2);
107189                 im.setAtLeastIfValid(label.getLocation(0, Position.RIGHT), label.getLocation(1, Position.RIGHT), 2);
107190               }
107191             } else { return GraphComponent$$1.prototype.updateIM.apply(this, arguments) }
107192           };
107193
107194           return Edge;
107195         }(GraphComponent));
107196
107197         var BufferBuilder = function BufferBuilder (bufParams) {
107198           this._workingPrecisionModel = null;
107199           this._workingNoder = null;
107200           this._geomFact = null;
107201           this._graph = null;
107202           this._edgeList = new EdgeList();
107203           this._bufParams = bufParams || null;
107204         };
107205         BufferBuilder.prototype.setWorkingPrecisionModel = function setWorkingPrecisionModel (pm) {
107206           this._workingPrecisionModel = pm;
107207         };
107208         BufferBuilder.prototype.insertUniqueEdge = function insertUniqueEdge (e) {
107209           var existingEdge = this._edgeList.findEqualEdge(e);
107210           if (existingEdge !== null) {
107211             var existingLabel = existingEdge.getLabel();
107212             var labelToMerge = e.getLabel();
107213             if (!existingEdge.isPointwiseEqual(e)) {
107214               labelToMerge = new Label(e.getLabel());
107215               labelToMerge.flip();
107216             }
107217             existingLabel.merge(labelToMerge);
107218             var mergeDelta = BufferBuilder.depthDelta(labelToMerge);
107219             var existingDelta = existingEdge.getDepthDelta();
107220             var newDelta = existingDelta + mergeDelta;
107221             existingEdge.setDepthDelta(newDelta);
107222           } else {
107223             this._edgeList.add(e);
107224             e.setDepthDelta(BufferBuilder.depthDelta(e.getLabel()));
107225           }
107226         };
107227         BufferBuilder.prototype.buildSubgraphs = function buildSubgraphs (subgraphList, polyBuilder) {
107228           var processedGraphs = new ArrayList();
107229           for (var i = subgraphList.iterator(); i.hasNext();) {
107230             var subgraph = i.next();
107231             var p = subgraph.getRightmostCoordinate();
107232             var locater = new SubgraphDepthLocater(processedGraphs);
107233             var outsideDepth = locater.getDepth(p);
107234             subgraph.computeDepth(outsideDepth);
107235             subgraph.findResultEdges();
107236             processedGraphs.add(subgraph);
107237             polyBuilder.add(subgraph.getDirectedEdges(), subgraph.getNodes());
107238           }
107239         };
107240         BufferBuilder.prototype.createSubgraphs = function createSubgraphs (graph) {
107241           var subgraphList = new ArrayList();
107242           for (var i = graph.getNodes().iterator(); i.hasNext();) {
107243             var node = i.next();
107244             if (!node.isVisited()) {
107245               var subgraph = new BufferSubgraph();
107246               subgraph.create(node);
107247               subgraphList.add(subgraph);
107248             }
107249           }
107250           Collections.sort(subgraphList, Collections.reverseOrder());
107251           return subgraphList
107252         };
107253         BufferBuilder.prototype.createEmptyResultGeometry = function createEmptyResultGeometry () {
107254           var emptyGeom = this._geomFact.createPolygon();
107255           return emptyGeom
107256         };
107257         BufferBuilder.prototype.getNoder = function getNoder (precisionModel) {
107258           if (this._workingNoder !== null) { return this._workingNoder }
107259           var noder = new MCIndexNoder();
107260           var li = new RobustLineIntersector();
107261           li.setPrecisionModel(precisionModel);
107262           noder.setSegmentIntersector(new IntersectionAdder(li));
107263           return noder
107264         };
107265         BufferBuilder.prototype.buffer = function buffer (g, distance) {
107266           var precisionModel = this._workingPrecisionModel;
107267           if (precisionModel === null) { precisionModel = g.getPrecisionModel(); }
107268           this._geomFact = g.getFactory();
107269           var curveBuilder = new OffsetCurveBuilder(precisionModel, this._bufParams);
107270           var curveSetBuilder = new OffsetCurveSetBuilder(g, distance, curveBuilder);
107271           var bufferSegStrList = curveSetBuilder.getCurves();
107272           if (bufferSegStrList.size() <= 0) {
107273             return this.createEmptyResultGeometry()
107274           }
107275           this.computeNodedEdges(bufferSegStrList, precisionModel);
107276           this._graph = new PlanarGraph(new OverlayNodeFactory());
107277           this._graph.addEdges(this._edgeList.getEdges());
107278           var subgraphList = this.createSubgraphs(this._graph);
107279           var polyBuilder = new PolygonBuilder(this._geomFact);
107280           this.buildSubgraphs(subgraphList, polyBuilder);
107281           var resultPolyList = polyBuilder.getPolygons();
107282           if (resultPolyList.size() <= 0) {
107283             return this.createEmptyResultGeometry()
107284           }
107285           var resultGeom = this._geomFact.buildGeometry(resultPolyList);
107286           return resultGeom
107287         };
107288         BufferBuilder.prototype.computeNodedEdges = function computeNodedEdges (bufferSegStrList, precisionModel) {
107289             var this$1 = this;
107290
107291           var noder = this.getNoder(precisionModel);
107292           noder.computeNodes(bufferSegStrList);
107293           var nodedSegStrings = noder.getNodedSubstrings();
107294           for (var i = nodedSegStrings.iterator(); i.hasNext();) {
107295             var segStr = i.next();
107296             var pts = segStr.getCoordinates();
107297             if (pts.length === 2 && pts[0].equals2D(pts[1])) { continue }
107298             var oldLabel = segStr.getData();
107299             var edge = new Edge(segStr.getCoordinates(), new Label(oldLabel));
107300             this$1.insertUniqueEdge(edge);
107301           }
107302         };
107303         BufferBuilder.prototype.setNoder = function setNoder (noder) {
107304           this._workingNoder = noder;
107305         };
107306         BufferBuilder.prototype.interfaces_ = function interfaces_ () {
107307           return []
107308         };
107309         BufferBuilder.prototype.getClass = function getClass () {
107310           return BufferBuilder
107311         };
107312         BufferBuilder.depthDelta = function depthDelta (label) {
107313           var lLoc = label.getLocation(0, Position.LEFT);
107314           var rLoc = label.getLocation(0, Position.RIGHT);
107315           if (lLoc === Location.INTERIOR && rLoc === Location.EXTERIOR) { return 1; } else if (lLoc === Location.EXTERIOR && rLoc === Location.INTERIOR) { return -1 }
107316           return 0
107317         };
107318         BufferBuilder.convertSegStrings = function convertSegStrings (it) {
107319           var fact = new GeometryFactory();
107320           var lines = new ArrayList();
107321           while (it.hasNext()) {
107322             var ss = it.next();
107323             var line = fact.createLineString(ss.getCoordinates());
107324             lines.add(line);
107325           }
107326           return fact.buildGeometry(lines)
107327         };
107328
107329         var ScaledNoder = function ScaledNoder () {
107330           this._noder = null;
107331           this._scaleFactor = null;
107332           this._offsetX = null;
107333           this._offsetY = null;
107334           this._isScaled = false;
107335           if (arguments.length === 2) {
107336             var noder = arguments[0];
107337             var scaleFactor = arguments[1];
107338             this._noder = noder;
107339             this._scaleFactor = scaleFactor;
107340             this._offsetX = 0.0;
107341             this._offsetY = 0.0;
107342             this._isScaled = !this.isIntegerPrecision();
107343           } else if (arguments.length === 4) {
107344             var noder$1 = arguments[0];
107345             var scaleFactor$1 = arguments[1];
107346             var offsetX = arguments[2];
107347             var offsetY = arguments[3];
107348             this._noder = noder$1;
107349             this._scaleFactor = scaleFactor$1;
107350             this._offsetX = offsetX;
107351             this._offsetY = offsetY;
107352             this._isScaled = !this.isIntegerPrecision();
107353           }
107354         };
107355         ScaledNoder.prototype.rescale = function rescale () {
107356             var this$1 = this;
107357
107358           if (hasInterface(arguments[0], Collection)) {
107359             var segStrings = arguments[0];
107360             for (var i = segStrings.iterator(); i.hasNext();) {
107361               var ss = i.next();
107362               this$1.rescale(ss.getCoordinates());
107363             }
107364           } else if (arguments[0] instanceof Array) {
107365             var pts = arguments[0];
107366             // let p0 = null
107367             // let p1 = null
107368             // if (pts.length === 2) {
107369             // p0 = new Coordinate(pts[0])
107370             // p1 = new Coordinate(pts[1])
107371             // }
107372             for (var i$1 = 0; i$1 < pts.length; i$1++) {
107373               pts[i$1].x = pts[i$1].x / this$1._scaleFactor + this$1._offsetX;
107374               pts[i$1].y = pts[i$1].y / this$1._scaleFactor + this$1._offsetY;
107375             }
107376             if (pts.length === 2 && pts[0].equals2D(pts[1])) {
107377               System.out.println(pts);
107378             }
107379           }
107380         };
107381         ScaledNoder.prototype.scale = function scale () {
107382             var this$1 = this;
107383
107384           if (hasInterface(arguments[0], Collection)) {
107385             var segStrings = arguments[0];
107386             var nodedSegmentStrings = new ArrayList();
107387             for (var i = segStrings.iterator(); i.hasNext();) {
107388               var ss = i.next();
107389               nodedSegmentStrings.add(new NodedSegmentString(this$1.scale(ss.getCoordinates()), ss.getData()));
107390             }
107391             return nodedSegmentStrings
107392           } else if (arguments[0] instanceof Array) {
107393             var pts = arguments[0];
107394             var roundPts = new Array(pts.length).fill(null);
107395             for (var i$1 = 0; i$1 < pts.length; i$1++) {
107396               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);
107397             }
107398             var roundPtsNoDup = CoordinateArrays.removeRepeatedPoints(roundPts);
107399             return roundPtsNoDup
107400           }
107401         };
107402         ScaledNoder.prototype.isIntegerPrecision = function isIntegerPrecision () {
107403           return this._scaleFactor === 1.0
107404         };
107405         ScaledNoder.prototype.getNodedSubstrings = function getNodedSubstrings () {
107406           var splitSS = this._noder.getNodedSubstrings();
107407           if (this._isScaled) { this.rescale(splitSS); }
107408           return splitSS
107409         };
107410         ScaledNoder.prototype.computeNodes = function computeNodes (inputSegStrings) {
107411           var intSegStrings = inputSegStrings;
107412           if (this._isScaled) { intSegStrings = this.scale(inputSegStrings); }
107413           this._noder.computeNodes(intSegStrings);
107414         };
107415         ScaledNoder.prototype.interfaces_ = function interfaces_ () {
107416           return [Noder]
107417         };
107418         ScaledNoder.prototype.getClass = function getClass () {
107419           return ScaledNoder
107420         };
107421
107422         var NodingValidator = function NodingValidator () {
107423           this._li = new RobustLineIntersector();
107424           this._segStrings = null;
107425           var segStrings = arguments[0];
107426           this._segStrings = segStrings;
107427         };
107428
107429         var staticAccessors$33 = { fact: { configurable: true } };
107430         NodingValidator.prototype.checkEndPtVertexIntersections = function checkEndPtVertexIntersections () {
107431             var this$1 = this;
107432
107433           if (arguments.length === 0) {
107434             for (var i = this._segStrings.iterator(); i.hasNext();) {
107435               var ss = i.next();
107436               var pts = ss.getCoordinates();
107437               this$1.checkEndPtVertexIntersections(pts[0], this$1._segStrings);
107438               this$1.checkEndPtVertexIntersections(pts[pts.length - 1], this$1._segStrings);
107439             }
107440           } else if (arguments.length === 2) {
107441             var testPt = arguments[0];
107442             var segStrings = arguments[1];
107443             for (var i$1 = segStrings.iterator(); i$1.hasNext();) {
107444               var ss$1 = i$1.next();
107445               var pts$1 = ss$1.getCoordinates();
107446               for (var j = 1; j < pts$1.length - 1; j++) {
107447                 if (pts$1[j].equals(testPt)) { throw new RuntimeException('found endpt/interior pt intersection at index ' + j + ' :pt ' + testPt) }
107448               }
107449             }
107450           }
107451         };
107452         NodingValidator.prototype.checkInteriorIntersections = function checkInteriorIntersections () {
107453             var this$1 = this;
107454
107455           if (arguments.length === 0) {
107456             for (var i = this._segStrings.iterator(); i.hasNext();) {
107457               var ss0 = i.next();
107458               for (var j = this._segStrings.iterator(); j.hasNext();) {
107459                 var ss1 = j.next();
107460                 this$1.checkInteriorIntersections(ss0, ss1);
107461               }
107462             }
107463           } else if (arguments.length === 2) {
107464             var ss0$1 = arguments[0];
107465             var ss1$1 = arguments[1];
107466             var pts0 = ss0$1.getCoordinates();
107467             var pts1 = ss1$1.getCoordinates();
107468             for (var i0 = 0; i0 < pts0.length - 1; i0++) {
107469               for (var i1 = 0; i1 < pts1.length - 1; i1++) {
107470                 this$1.checkInteriorIntersections(ss0$1, i0, ss1$1, i1);
107471               }
107472             }
107473           } else if (arguments.length === 4) {
107474             var e0 = arguments[0];
107475             var segIndex0 = arguments[1];
107476             var e1 = arguments[2];
107477             var segIndex1 = arguments[3];
107478             if (e0 === e1 && segIndex0 === segIndex1) { return null }
107479             var p00 = e0.getCoordinates()[segIndex0];
107480             var p01 = e0.getCoordinates()[segIndex0 + 1];
107481             var p10 = e1.getCoordinates()[segIndex1];
107482             var p11 = e1.getCoordinates()[segIndex1 + 1];
107483             this._li.computeIntersection(p00, p01, p10, p11);
107484             if (this._li.hasIntersection()) {
107485               if (this._li.isProper() || this.hasInteriorIntersection(this._li, p00, p01) || this.hasInteriorIntersection(this._li, p10, p11)) {
107486                 throw new RuntimeException('found non-noded intersection at ' + p00 + '-' + p01 + ' and ' + p10 + '-' + p11)
107487               }
107488             }
107489           }
107490         };
107491         NodingValidator.prototype.checkValid = function checkValid () {
107492           this.checkEndPtVertexIntersections();
107493           this.checkInteriorIntersections();
107494           this.checkCollapses();
107495         };
107496         NodingValidator.prototype.checkCollapses = function checkCollapses () {
107497             var this$1 = this;
107498
107499           if (arguments.length === 0) {
107500             for (var i = this._segStrings.iterator(); i.hasNext();) {
107501               var ss = i.next();
107502               this$1.checkCollapses(ss);
107503             }
107504           } else if (arguments.length === 1) {
107505             var ss$1 = arguments[0];
107506             var pts = ss$1.getCoordinates();
107507             for (var i$1 = 0; i$1 < pts.length - 2; i$1++) {
107508               this$1.checkCollapse(pts[i$1], pts[i$1 + 1], pts[i$1 + 2]);
107509             }
107510           }
107511         };
107512         NodingValidator.prototype.hasInteriorIntersection = function hasInteriorIntersection (li, p0, p1) {
107513           for (var i = 0; i < li.getIntersectionNum(); i++) {
107514             var intPt = li.getIntersection(i);
107515             if (!(intPt.equals(p0) || intPt.equals(p1))) { return true }
107516           }
107517           return false
107518         };
107519         NodingValidator.prototype.checkCollapse = function checkCollapse (p0, p1, p2) {
107520           if (p0.equals(p2)) { throw new RuntimeException('found non-noded collapse at ' + NodingValidator.fact.createLineString([p0, p1, p2])) }
107521         };
107522         NodingValidator.prototype.interfaces_ = function interfaces_ () {
107523           return []
107524         };
107525         NodingValidator.prototype.getClass = function getClass () {
107526           return NodingValidator
107527         };
107528         staticAccessors$33.fact.get = function () { return new GeometryFactory() };
107529
107530         Object.defineProperties( NodingValidator, staticAccessors$33 );
107531
107532         var HotPixel = function HotPixel () {
107533           this._li = null;
107534           this._pt = null;
107535           this._originalPt = null;
107536           this._ptScaled = null;
107537           this._p0Scaled = null;
107538           this._p1Scaled = null;
107539           this._scaleFactor = null;
107540           this._minx = null;
107541           this._maxx = null;
107542           this._miny = null;
107543           this._maxy = null;
107544           this._corner = new Array(4).fill(null);
107545           this._safeEnv = null;
107546           var pt = arguments[0];
107547           var scaleFactor = arguments[1];
107548           var li = arguments[2];
107549           this._originalPt = pt;
107550           this._pt = pt;
107551           this._scaleFactor = scaleFactor;
107552           this._li = li;
107553           if (scaleFactor <= 0) { throw new IllegalArgumentException('Scale factor must be non-zero') }
107554           if (scaleFactor !== 1.0) {
107555             this._pt = new Coordinate(this.scale(pt.x), this.scale(pt.y));
107556             this._p0Scaled = new Coordinate();
107557             this._p1Scaled = new Coordinate();
107558           }
107559           this.initCorners(this._pt);
107560         };
107561
107562         var staticAccessors$34 = { SAFE_ENV_EXPANSION_FACTOR: { configurable: true } };
107563         HotPixel.prototype.intersectsScaled = function intersectsScaled (p0, p1) {
107564           var segMinx = Math.min(p0.x, p1.x);
107565           var segMaxx = Math.max(p0.x, p1.x);
107566           var segMiny = Math.min(p0.y, p1.y);
107567           var segMaxy = Math.max(p0.y, p1.y);
107568           var isOutsidePixelEnv = this._maxx < segMinx || this._minx > segMaxx || this._maxy < segMiny || this._miny > segMaxy;
107569           if (isOutsidePixelEnv) { return false }
107570           var intersects = this.intersectsToleranceSquare(p0, p1);
107571           Assert.isTrue(!(isOutsidePixelEnv && intersects), 'Found bad envelope test');
107572           return intersects
107573         };
107574         HotPixel.prototype.initCorners = function initCorners (pt) {
107575           var tolerance = 0.5;
107576           this._minx = pt.x - tolerance;
107577           this._maxx = pt.x + tolerance;
107578           this._miny = pt.y - tolerance;
107579           this._maxy = pt.y + tolerance;
107580           this._corner[0] = new Coordinate(this._maxx, this._maxy);
107581           this._corner[1] = new Coordinate(this._minx, this._maxy);
107582           this._corner[2] = new Coordinate(this._minx, this._miny);
107583           this._corner[3] = new Coordinate(this._maxx, this._miny);
107584         };
107585         HotPixel.prototype.intersects = function intersects (p0, p1) {
107586           if (this._scaleFactor === 1.0) { return this.intersectsScaled(p0, p1) }
107587           this.copyScaled(p0, this._p0Scaled);
107588           this.copyScaled(p1, this._p1Scaled);
107589           return this.intersectsScaled(this._p0Scaled, this._p1Scaled)
107590         };
107591         HotPixel.prototype.scale = function scale (val) {
107592           return Math.round(val * this._scaleFactor)
107593         };
107594         HotPixel.prototype.getCoordinate = function getCoordinate () {
107595           return this._originalPt
107596         };
107597         HotPixel.prototype.copyScaled = function copyScaled (p, pScaled) {
107598           pScaled.x = this.scale(p.x);
107599           pScaled.y = this.scale(p.y);
107600         };
107601         HotPixel.prototype.getSafeEnvelope = function getSafeEnvelope () {
107602           if (this._safeEnv === null) {
107603             var safeTolerance = HotPixel.SAFE_ENV_EXPANSION_FACTOR / this._scaleFactor;
107604             this._safeEnv = new Envelope(this._originalPt.x - safeTolerance, this._originalPt.x + safeTolerance, this._originalPt.y - safeTolerance, this._originalPt.y + safeTolerance);
107605           }
107606           return this._safeEnv
107607         };
107608         HotPixel.prototype.intersectsPixelClosure = function intersectsPixelClosure (p0, p1) {
107609           this._li.computeIntersection(p0, p1, this._corner[0], this._corner[1]);
107610           if (this._li.hasIntersection()) { return true }
107611           this._li.computeIntersection(p0, p1, this._corner[1], this._corner[2]);
107612           if (this._li.hasIntersection()) { return true }
107613           this._li.computeIntersection(p0, p1, this._corner[2], this._corner[3]);
107614           if (this._li.hasIntersection()) { return true }
107615           this._li.computeIntersection(p0, p1, this._corner[3], this._corner[0]);
107616           if (this._li.hasIntersection()) { return true }
107617           return false
107618         };
107619         HotPixel.prototype.intersectsToleranceSquare = function intersectsToleranceSquare (p0, p1) {
107620           var intersectsLeft = false;
107621           var intersectsBottom = false;
107622           this._li.computeIntersection(p0, p1, this._corner[0], this._corner[1]);
107623           if (this._li.isProper()) { return true }
107624           this._li.computeIntersection(p0, p1, this._corner[1], this._corner[2]);
107625           if (this._li.isProper()) { return true }
107626           if (this._li.hasIntersection()) { intersectsLeft = true; }
107627           this._li.computeIntersection(p0, p1, this._corner[2], this._corner[3]);
107628           if (this._li.isProper()) { return true }
107629           if (this._li.hasIntersection()) { intersectsBottom = true; }
107630           this._li.computeIntersection(p0, p1, this._corner[3], this._corner[0]);
107631           if (this._li.isProper()) { return true }
107632           if (intersectsLeft && intersectsBottom) { return true }
107633           if (p0.equals(this._pt)) { return true }
107634           if (p1.equals(this._pt)) { return true }
107635           return false
107636         };
107637         HotPixel.prototype.addSnappedNode = function addSnappedNode (segStr, segIndex) {
107638           var p0 = segStr.getCoordinate(segIndex);
107639           var p1 = segStr.getCoordinate(segIndex + 1);
107640           if (this.intersects(p0, p1)) {
107641             segStr.addIntersection(this.getCoordinate(), segIndex);
107642             return true
107643           }
107644           return false
107645         };
107646         HotPixel.prototype.interfaces_ = function interfaces_ () {
107647           return []
107648         };
107649         HotPixel.prototype.getClass = function getClass () {
107650           return HotPixel
107651         };
107652         staticAccessors$34.SAFE_ENV_EXPANSION_FACTOR.get = function () { return 0.75 };
107653
107654         Object.defineProperties( HotPixel, staticAccessors$34 );
107655
107656         var MonotoneChainSelectAction = function MonotoneChainSelectAction () {
107657           this.tempEnv1 = new Envelope();
107658           this.selectedSegment = new LineSegment();
107659         };
107660         MonotoneChainSelectAction.prototype.select = function select () {
107661           if (arguments.length === 1) ; else if (arguments.length === 2) {
107662             var mc = arguments[0];
107663             var startIndex = arguments[1];
107664             mc.getLineSegment(startIndex, this.selectedSegment);
107665             this.select(this.selectedSegment);
107666           }
107667         };
107668         MonotoneChainSelectAction.prototype.interfaces_ = function interfaces_ () {
107669           return []
107670         };
107671         MonotoneChainSelectAction.prototype.getClass = function getClass () {
107672           return MonotoneChainSelectAction
107673         };
107674
107675         var MCIndexPointSnapper = function MCIndexPointSnapper () {
107676           this._index = null;
107677           var index = arguments[0];
107678           this._index = index;
107679         };
107680
107681         var staticAccessors$35 = { HotPixelSnapAction: { configurable: true } };
107682         MCIndexPointSnapper.prototype.snap = function snap () {
107683           if (arguments.length === 1) {
107684             var hotPixel = arguments[0];
107685             return this.snap(hotPixel, null, -1)
107686           } else if (arguments.length === 3) {
107687             var hotPixel$1 = arguments[0];
107688             var parentEdge = arguments[1];
107689             var hotPixelVertexIndex = arguments[2];
107690             var pixelEnv = hotPixel$1.getSafeEnvelope();
107691             var hotPixelSnapAction = new HotPixelSnapAction(hotPixel$1, parentEdge, hotPixelVertexIndex);
107692             this._index.query(pixelEnv, {
107693               interfaces_: function () {
107694                 return [ItemVisitor]
107695               },
107696               visitItem: function (item) {
107697                 var testChain = item;
107698                 testChain.select(pixelEnv, hotPixelSnapAction);
107699               }
107700             });
107701             return hotPixelSnapAction.isNodeAdded()
107702           }
107703         };
107704         MCIndexPointSnapper.prototype.interfaces_ = function interfaces_ () {
107705           return []
107706         };
107707         MCIndexPointSnapper.prototype.getClass = function getClass () {
107708           return MCIndexPointSnapper
107709         };
107710         staticAccessors$35.HotPixelSnapAction.get = function () { return HotPixelSnapAction };
107711
107712         Object.defineProperties( MCIndexPointSnapper, staticAccessors$35 );
107713
107714         var HotPixelSnapAction = (function (MonotoneChainSelectAction$$1) {
107715           function HotPixelSnapAction () {
107716             MonotoneChainSelectAction$$1.call(this);
107717             this._hotPixel = null;
107718             this._parentEdge = null;
107719             this._hotPixelVertexIndex = null;
107720             this._isNodeAdded = false;
107721             var hotPixel = arguments[0];
107722             var parentEdge = arguments[1];
107723             var hotPixelVertexIndex = arguments[2];
107724             this._hotPixel = hotPixel;
107725             this._parentEdge = parentEdge;
107726             this._hotPixelVertexIndex = hotPixelVertexIndex;
107727           }
107728
107729           if ( MonotoneChainSelectAction$$1 ) { HotPixelSnapAction.__proto__ = MonotoneChainSelectAction$$1; }
107730           HotPixelSnapAction.prototype = Object.create( MonotoneChainSelectAction$$1 && MonotoneChainSelectAction$$1.prototype );
107731           HotPixelSnapAction.prototype.constructor = HotPixelSnapAction;
107732           HotPixelSnapAction.prototype.isNodeAdded = function isNodeAdded () {
107733             return this._isNodeAdded
107734           };
107735           HotPixelSnapAction.prototype.select = function select () {
107736             if (arguments.length === 2) {
107737               var mc = arguments[0];
107738               var startIndex = arguments[1];
107739               var ss = mc.getContext();
107740               if (this._parentEdge !== null) {
107741                 if (ss === this._parentEdge && startIndex === this._hotPixelVertexIndex) { return null }
107742               }
107743               this._isNodeAdded = this._hotPixel.addSnappedNode(ss, startIndex);
107744             } else { return MonotoneChainSelectAction$$1.prototype.select.apply(this, arguments) }
107745           };
107746           HotPixelSnapAction.prototype.interfaces_ = function interfaces_ () {
107747             return []
107748           };
107749           HotPixelSnapAction.prototype.getClass = function getClass () {
107750             return HotPixelSnapAction
107751           };
107752
107753           return HotPixelSnapAction;
107754         }(MonotoneChainSelectAction));
107755
107756         var InteriorIntersectionFinderAdder = function InteriorIntersectionFinderAdder () {
107757           this._li = null;
107758           this._interiorIntersections = null;
107759           var li = arguments[0];
107760           this._li = li;
107761           this._interiorIntersections = new ArrayList();
107762         };
107763         InteriorIntersectionFinderAdder.prototype.processIntersections = function processIntersections (e0, segIndex0, e1, segIndex1) {
107764             var this$1 = this;
107765
107766           if (e0 === e1 && segIndex0 === segIndex1) { return null }
107767           var p00 = e0.getCoordinates()[segIndex0];
107768           var p01 = e0.getCoordinates()[segIndex0 + 1];
107769           var p10 = e1.getCoordinates()[segIndex1];
107770           var p11 = e1.getCoordinates()[segIndex1 + 1];
107771           this._li.computeIntersection(p00, p01, p10, p11);
107772           if (this._li.hasIntersection()) {
107773             if (this._li.isInteriorIntersection()) {
107774               for (var intIndex = 0; intIndex < this._li.getIntersectionNum(); intIndex++) {
107775                 this$1._interiorIntersections.add(this$1._li.getIntersection(intIndex));
107776               }
107777               e0.addIntersections(this._li, segIndex0, 0);
107778               e1.addIntersections(this._li, segIndex1, 1);
107779             }
107780           }
107781         };
107782         InteriorIntersectionFinderAdder.prototype.isDone = function isDone () {
107783           return false
107784         };
107785         InteriorIntersectionFinderAdder.prototype.getInteriorIntersections = function getInteriorIntersections () {
107786           return this._interiorIntersections
107787         };
107788         InteriorIntersectionFinderAdder.prototype.interfaces_ = function interfaces_ () {
107789           return [SegmentIntersector]
107790         };
107791         InteriorIntersectionFinderAdder.prototype.getClass = function getClass () {
107792           return InteriorIntersectionFinderAdder
107793         };
107794
107795         var MCIndexSnapRounder = function MCIndexSnapRounder () {
107796           this._pm = null;
107797           this._li = null;
107798           this._scaleFactor = null;
107799           this._noder = null;
107800           this._pointSnapper = null;
107801           this._nodedSegStrings = null;
107802           var pm = arguments[0];
107803           this._pm = pm;
107804           this._li = new RobustLineIntersector();
107805           this._li.setPrecisionModel(pm);
107806           this._scaleFactor = pm.getScale();
107807         };
107808         MCIndexSnapRounder.prototype.checkCorrectness = function checkCorrectness (inputSegmentStrings) {
107809           var resultSegStrings = NodedSegmentString.getNodedSubstrings(inputSegmentStrings);
107810           var nv = new NodingValidator(resultSegStrings);
107811           try {
107812             nv.checkValid();
107813           } catch (ex) {
107814             if (ex instanceof Exception) {
107815               ex.printStackTrace();
107816             } else { throw ex }
107817           } finally {}
107818         };
107819         MCIndexSnapRounder.prototype.getNodedSubstrings = function getNodedSubstrings () {
107820           return NodedSegmentString.getNodedSubstrings(this._nodedSegStrings)
107821         };
107822         MCIndexSnapRounder.prototype.snapRound = function snapRound (segStrings, li) {
107823           var intersections = this.findInteriorIntersections(segStrings, li);
107824           this.computeIntersectionSnaps(intersections);
107825           this.computeVertexSnaps(segStrings);
107826         };
107827         MCIndexSnapRounder.prototype.findInteriorIntersections = function findInteriorIntersections (segStrings, li) {
107828           var intFinderAdder = new InteriorIntersectionFinderAdder(li);
107829           this._noder.setSegmentIntersector(intFinderAdder);
107830           this._noder.computeNodes(segStrings);
107831           return intFinderAdder.getInteriorIntersections()
107832         };
107833         MCIndexSnapRounder.prototype.computeVertexSnaps = function computeVertexSnaps () {
107834             var this$1 = this;
107835
107836           if (hasInterface(arguments[0], Collection)) {
107837             var edges = arguments[0];
107838             for (var i0 = edges.iterator(); i0.hasNext();) {
107839               var edge0 = i0.next();
107840               this$1.computeVertexSnaps(edge0);
107841             }
107842           } else if (arguments[0] instanceof NodedSegmentString) {
107843             var e = arguments[0];
107844             var pts0 = e.getCoordinates();
107845             for (var i = 0; i < pts0.length; i++) {
107846               var hotPixel = new HotPixel(pts0[i], this$1._scaleFactor, this$1._li);
107847               var isNodeAdded = this$1._pointSnapper.snap(hotPixel, e, i);
107848               if (isNodeAdded) {
107849                 e.addIntersection(pts0[i], i);
107850               }
107851             }
107852           }
107853         };
107854         MCIndexSnapRounder.prototype.computeNodes = function computeNodes (inputSegmentStrings) {
107855           this._nodedSegStrings = inputSegmentStrings;
107856           this._noder = new MCIndexNoder();
107857           this._pointSnapper = new MCIndexPointSnapper(this._noder.getIndex());
107858           this.snapRound(inputSegmentStrings, this._li);
107859         };
107860         MCIndexSnapRounder.prototype.computeIntersectionSnaps = function computeIntersectionSnaps (snapPts) {
107861             var this$1 = this;
107862
107863           for (var it = snapPts.iterator(); it.hasNext();) {
107864             var snapPt = it.next();
107865             var hotPixel = new HotPixel(snapPt, this$1._scaleFactor, this$1._li);
107866             this$1._pointSnapper.snap(hotPixel);
107867           }
107868         };
107869         MCIndexSnapRounder.prototype.interfaces_ = function interfaces_ () {
107870           return [Noder]
107871         };
107872         MCIndexSnapRounder.prototype.getClass = function getClass () {
107873           return MCIndexSnapRounder
107874         };
107875
107876         var BufferOp = function BufferOp () {
107877           this._argGeom = null;
107878           this._distance = null;
107879           this._bufParams = new BufferParameters();
107880           this._resultGeometry = null;
107881           this._saveException = null;
107882           if (arguments.length === 1) {
107883             var g = arguments[0];
107884             this._argGeom = g;
107885           } else if (arguments.length === 2) {
107886             var g$1 = arguments[0];
107887             var bufParams = arguments[1];
107888             this._argGeom = g$1;
107889             this._bufParams = bufParams;
107890           }
107891         };
107892
107893         var staticAccessors$32 = { CAP_ROUND: { configurable: true },CAP_BUTT: { configurable: true },CAP_FLAT: { configurable: true },CAP_SQUARE: { configurable: true },MAX_PRECISION_DIGITS: { configurable: true } };
107894         BufferOp.prototype.bufferFixedPrecision = function bufferFixedPrecision (fixedPM) {
107895           var noder = new ScaledNoder(new MCIndexSnapRounder(new PrecisionModel(1.0)), fixedPM.getScale());
107896           var bufBuilder = new BufferBuilder(this._bufParams);
107897           bufBuilder.setWorkingPrecisionModel(fixedPM);
107898           bufBuilder.setNoder(noder);
107899           this._resultGeometry = bufBuilder.buffer(this._argGeom, this._distance);
107900         };
107901         BufferOp.prototype.bufferReducedPrecision = function bufferReducedPrecision () {
107902             var this$1 = this;
107903
107904           if (arguments.length === 0) {
107905             for (var precDigits = BufferOp.MAX_PRECISION_DIGITS; precDigits >= 0; precDigits--) {
107906               try {
107907                 this$1.bufferReducedPrecision(precDigits);
107908               } catch (ex) {
107909                 if (ex instanceof TopologyException) {
107910                   this$1._saveException = ex;
107911                 } else { throw ex }
107912               } finally {}
107913               if (this$1._resultGeometry !== null) { return null }
107914             }
107915             throw this._saveException
107916           } else if (arguments.length === 1) {
107917             var precisionDigits = arguments[0];
107918             var sizeBasedScaleFactor = BufferOp.precisionScaleFactor(this._argGeom, this._distance, precisionDigits);
107919             var fixedPM = new PrecisionModel(sizeBasedScaleFactor);
107920             this.bufferFixedPrecision(fixedPM);
107921           }
107922         };
107923         BufferOp.prototype.computeGeometry = function computeGeometry () {
107924           this.bufferOriginalPrecision();
107925           if (this._resultGeometry !== null) { return null }
107926           var argPM = this._argGeom.getFactory().getPrecisionModel();
107927           if (argPM.getType() === PrecisionModel.FIXED) { this.bufferFixedPrecision(argPM); } else { this.bufferReducedPrecision(); }
107928         };
107929         BufferOp.prototype.setQuadrantSegments = function setQuadrantSegments (quadrantSegments) {
107930           this._bufParams.setQuadrantSegments(quadrantSegments);
107931         };
107932         BufferOp.prototype.bufferOriginalPrecision = function bufferOriginalPrecision () {
107933           try {
107934             var bufBuilder = new BufferBuilder(this._bufParams);
107935             this._resultGeometry = bufBuilder.buffer(this._argGeom, this._distance);
107936           } catch (ex) {
107937             if (ex instanceof RuntimeException) {
107938               this._saveException = ex;
107939             } else { throw ex }
107940           } finally {}
107941         };
107942         BufferOp.prototype.getResultGeometry = function getResultGeometry (distance) {
107943           this._distance = distance;
107944           this.computeGeometry();
107945           return this._resultGeometry
107946         };
107947         BufferOp.prototype.setEndCapStyle = function setEndCapStyle (endCapStyle) {
107948           this._bufParams.setEndCapStyle(endCapStyle);
107949         };
107950         BufferOp.prototype.interfaces_ = function interfaces_ () {
107951           return []
107952         };
107953         BufferOp.prototype.getClass = function getClass () {
107954           return BufferOp
107955         };
107956         BufferOp.bufferOp = function bufferOp () {
107957           if (arguments.length === 2) {
107958             var g = arguments[0];
107959             var distance = arguments[1];
107960             var gBuf = new BufferOp(g);
107961             var geomBuf = gBuf.getResultGeometry(distance);
107962             return geomBuf
107963           } else if (arguments.length === 3) {
107964             if (Number.isInteger(arguments[2]) && (arguments[0] instanceof Geometry && typeof arguments[1] === 'number')) {
107965               var g$1 = arguments[0];
107966               var distance$1 = arguments[1];
107967               var quadrantSegments = arguments[2];
107968               var bufOp = new BufferOp(g$1);
107969               bufOp.setQuadrantSegments(quadrantSegments);
107970               var geomBuf$1 = bufOp.getResultGeometry(distance$1);
107971               return geomBuf$1
107972             } else if (arguments[2] instanceof BufferParameters && (arguments[0] instanceof Geometry && typeof arguments[1] === 'number')) {
107973               var g$2 = arguments[0];
107974               var distance$2 = arguments[1];
107975               var params = arguments[2];
107976               var bufOp$1 = new BufferOp(g$2, params);
107977               var geomBuf$2 = bufOp$1.getResultGeometry(distance$2);
107978               return geomBuf$2
107979             }
107980           } else if (arguments.length === 4) {
107981             var g$3 = arguments[0];
107982             var distance$3 = arguments[1];
107983             var quadrantSegments$1 = arguments[2];
107984             var endCapStyle = arguments[3];
107985             var bufOp$2 = new BufferOp(g$3);
107986             bufOp$2.setQuadrantSegments(quadrantSegments$1);
107987             bufOp$2.setEndCapStyle(endCapStyle);
107988             var geomBuf$3 = bufOp$2.getResultGeometry(distance$3);
107989             return geomBuf$3
107990           }
107991         };
107992         BufferOp.precisionScaleFactor = function precisionScaleFactor (g, distance, maxPrecisionDigits) {
107993           var env = g.getEnvelopeInternal();
107994           var envMax = MathUtil.max(Math.abs(env.getMaxX()), Math.abs(env.getMaxY()), Math.abs(env.getMinX()), Math.abs(env.getMinY()));
107995           var expandByDistance = distance > 0.0 ? distance : 0.0;
107996           var bufEnvMax = envMax + 2 * expandByDistance;
107997           var bufEnvPrecisionDigits = Math.trunc(Math.log(bufEnvMax) / Math.log(10) + 1.0);
107998           var minUnitLog10 = maxPrecisionDigits - bufEnvPrecisionDigits;
107999           var scaleFactor = Math.pow(10.0, minUnitLog10);
108000           return scaleFactor
108001         };
108002         staticAccessors$32.CAP_ROUND.get = function () { return BufferParameters.CAP_ROUND };
108003         staticAccessors$32.CAP_BUTT.get = function () { return BufferParameters.CAP_FLAT };
108004         staticAccessors$32.CAP_FLAT.get = function () { return BufferParameters.CAP_FLAT };
108005         staticAccessors$32.CAP_SQUARE.get = function () { return BufferParameters.CAP_SQUARE };
108006         staticAccessors$32.MAX_PRECISION_DIGITS.get = function () { return 12 };
108007
108008         Object.defineProperties( BufferOp, staticAccessors$32 );
108009
108010         var PointPairDistance = function PointPairDistance () {
108011           this._pt = [new Coordinate(), new Coordinate()];
108012           this._distance = Double.NaN;
108013           this._isNull = true;
108014         };
108015         PointPairDistance.prototype.getCoordinates = function getCoordinates () {
108016           return this._pt
108017         };
108018         PointPairDistance.prototype.getCoordinate = function getCoordinate (i) {
108019           return this._pt[i]
108020         };
108021         PointPairDistance.prototype.setMinimum = function setMinimum () {
108022           if (arguments.length === 1) {
108023             var ptDist = arguments[0];
108024             this.setMinimum(ptDist._pt[0], ptDist._pt[1]);
108025           } else if (arguments.length === 2) {
108026             var p0 = arguments[0];
108027             var p1 = arguments[1];
108028             if (this._isNull) {
108029               this.initialize(p0, p1);
108030               return null
108031             }
108032             var dist = p0.distance(p1);
108033             if (dist < this._distance) { this.initialize(p0, p1, dist); }
108034           }
108035         };
108036         PointPairDistance.prototype.initialize = function initialize () {
108037           if (arguments.length === 0) {
108038             this._isNull = true;
108039           } else if (arguments.length === 2) {
108040             var p0 = arguments[0];
108041             var p1 = arguments[1];
108042             this._pt[0].setCoordinate(p0);
108043             this._pt[1].setCoordinate(p1);
108044             this._distance = p0.distance(p1);
108045             this._isNull = false;
108046           } else if (arguments.length === 3) {
108047             var p0$1 = arguments[0];
108048             var p1$1 = arguments[1];
108049             var distance = arguments[2];
108050             this._pt[0].setCoordinate(p0$1);
108051             this._pt[1].setCoordinate(p1$1);
108052             this._distance = distance;
108053             this._isNull = false;
108054           }
108055         };
108056         PointPairDistance.prototype.getDistance = function getDistance () {
108057           return this._distance
108058         };
108059         PointPairDistance.prototype.setMaximum = function setMaximum () {
108060           if (arguments.length === 1) {
108061             var ptDist = arguments[0];
108062             this.setMaximum(ptDist._pt[0], ptDist._pt[1]);
108063           } else if (arguments.length === 2) {
108064             var p0 = arguments[0];
108065             var p1 = arguments[1];
108066             if (this._isNull) {
108067               this.initialize(p0, p1);
108068               return null
108069             }
108070             var dist = p0.distance(p1);
108071             if (dist > this._distance) { this.initialize(p0, p1, dist); }
108072           }
108073         };
108074         PointPairDistance.prototype.interfaces_ = function interfaces_ () {
108075           return []
108076         };
108077         PointPairDistance.prototype.getClass = function getClass () {
108078           return PointPairDistance
108079         };
108080
108081         var DistanceToPointFinder = function DistanceToPointFinder () {};
108082
108083         DistanceToPointFinder.prototype.interfaces_ = function interfaces_ () {
108084           return []
108085         };
108086         DistanceToPointFinder.prototype.getClass = function getClass () {
108087           return DistanceToPointFinder
108088         };
108089         DistanceToPointFinder.computeDistance = function computeDistance () {
108090           if (arguments[2] instanceof PointPairDistance && (arguments[0] instanceof LineString && arguments[1] instanceof Coordinate)) {
108091             var line = arguments[0];
108092             var pt = arguments[1];
108093             var ptDist = arguments[2];
108094             var coords = line.getCoordinates();
108095             var tempSegment = new LineSegment();
108096             for (var i = 0; i < coords.length - 1; i++) {
108097               tempSegment.setCoordinates(coords[i], coords[i + 1]);
108098               var closestPt = tempSegment.closestPoint(pt);
108099               ptDist.setMinimum(closestPt, pt);
108100             }
108101           } else if (arguments[2] instanceof PointPairDistance && (arguments[0] instanceof Polygon && arguments[1] instanceof Coordinate)) {
108102             var poly = arguments[0];
108103             var pt$1 = arguments[1];
108104             var ptDist$1 = arguments[2];
108105             DistanceToPointFinder.computeDistance(poly.getExteriorRing(), pt$1, ptDist$1);
108106             for (var i$1 = 0; i$1 < poly.getNumInteriorRing(); i$1++) {
108107               DistanceToPointFinder.computeDistance(poly.getInteriorRingN(i$1), pt$1, ptDist$1);
108108             }
108109           } else if (arguments[2] instanceof PointPairDistance && (arguments[0] instanceof Geometry && arguments[1] instanceof Coordinate)) {
108110             var geom = arguments[0];
108111             var pt$2 = arguments[1];
108112             var ptDist$2 = arguments[2];
108113             if (geom instanceof LineString) {
108114               DistanceToPointFinder.computeDistance(geom, pt$2, ptDist$2);
108115             } else if (geom instanceof Polygon) {
108116               DistanceToPointFinder.computeDistance(geom, pt$2, ptDist$2);
108117             } else if (geom instanceof GeometryCollection) {
108118               var gc = geom;
108119               for (var i$2 = 0; i$2 < gc.getNumGeometries(); i$2++) {
108120                 var g = gc.getGeometryN(i$2);
108121                 DistanceToPointFinder.computeDistance(g, pt$2, ptDist$2);
108122               }
108123             } else {
108124               ptDist$2.setMinimum(geom.getCoordinate(), pt$2);
108125             }
108126           } else if (arguments[2] instanceof PointPairDistance && (arguments[0] instanceof LineSegment && arguments[1] instanceof Coordinate)) {
108127             var segment = arguments[0];
108128             var pt$3 = arguments[1];
108129             var ptDist$3 = arguments[2];
108130             var closestPt$1 = segment.closestPoint(pt$3);
108131             ptDist$3.setMinimum(closestPt$1, pt$3);
108132           }
108133         };
108134
108135         var BufferCurveMaximumDistanceFinder = function BufferCurveMaximumDistanceFinder (inputGeom) {
108136           this._maxPtDist = new PointPairDistance();
108137           this._inputGeom = inputGeom || null;
108138         };
108139
108140         var staticAccessors$36 = { MaxPointDistanceFilter: { configurable: true },MaxMidpointDistanceFilter: { configurable: true } };
108141         BufferCurveMaximumDistanceFinder.prototype.computeMaxMidpointDistance = function computeMaxMidpointDistance (curve) {
108142           var distFilter = new MaxMidpointDistanceFilter(this._inputGeom);
108143           curve.apply(distFilter);
108144           this._maxPtDist.setMaximum(distFilter.getMaxPointDistance());
108145         };
108146         BufferCurveMaximumDistanceFinder.prototype.computeMaxVertexDistance = function computeMaxVertexDistance (curve) {
108147           var distFilter = new MaxPointDistanceFilter(this._inputGeom);
108148           curve.apply(distFilter);
108149           this._maxPtDist.setMaximum(distFilter.getMaxPointDistance());
108150         };
108151         BufferCurveMaximumDistanceFinder.prototype.findDistance = function findDistance (bufferCurve) {
108152           this.computeMaxVertexDistance(bufferCurve);
108153           this.computeMaxMidpointDistance(bufferCurve);
108154           return this._maxPtDist.getDistance()
108155         };
108156         BufferCurveMaximumDistanceFinder.prototype.getDistancePoints = function getDistancePoints () {
108157           return this._maxPtDist
108158         };
108159         BufferCurveMaximumDistanceFinder.prototype.interfaces_ = function interfaces_ () {
108160           return []
108161         };
108162         BufferCurveMaximumDistanceFinder.prototype.getClass = function getClass () {
108163           return BufferCurveMaximumDistanceFinder
108164         };
108165         staticAccessors$36.MaxPointDistanceFilter.get = function () { return MaxPointDistanceFilter };
108166         staticAccessors$36.MaxMidpointDistanceFilter.get = function () { return MaxMidpointDistanceFilter };
108167
108168         Object.defineProperties( BufferCurveMaximumDistanceFinder, staticAccessors$36 );
108169
108170         var MaxPointDistanceFilter = function MaxPointDistanceFilter (geom) {
108171           this._maxPtDist = new PointPairDistance();
108172           this._minPtDist = new PointPairDistance();
108173           this._geom = geom || null;
108174         };
108175         MaxPointDistanceFilter.prototype.filter = function filter (pt) {
108176           this._minPtDist.initialize();
108177           DistanceToPointFinder.computeDistance(this._geom, pt, this._minPtDist);
108178           this._maxPtDist.setMaximum(this._minPtDist);
108179         };
108180         MaxPointDistanceFilter.prototype.getMaxPointDistance = function getMaxPointDistance () {
108181           return this._maxPtDist
108182         };
108183         MaxPointDistanceFilter.prototype.interfaces_ = function interfaces_ () {
108184           return [CoordinateFilter]
108185         };
108186         MaxPointDistanceFilter.prototype.getClass = function getClass () {
108187           return MaxPointDistanceFilter
108188         };
108189
108190         var MaxMidpointDistanceFilter = function MaxMidpointDistanceFilter (geom) {
108191           this._maxPtDist = new PointPairDistance();
108192           this._minPtDist = new PointPairDistance();
108193           this._geom = geom || null;
108194         };
108195         MaxMidpointDistanceFilter.prototype.filter = function filter (seq, index) {
108196           if (index === 0) { return null }
108197           var p0 = seq.getCoordinate(index - 1);
108198           var p1 = seq.getCoordinate(index);
108199           var midPt = new Coordinate((p0.x + p1.x) / 2, (p0.y + p1.y) / 2);
108200           this._minPtDist.initialize();
108201           DistanceToPointFinder.computeDistance(this._geom, midPt, this._minPtDist);
108202           this._maxPtDist.setMaximum(this._minPtDist);
108203         };
108204         MaxMidpointDistanceFilter.prototype.isDone = function isDone () {
108205           return false
108206         };
108207         MaxMidpointDistanceFilter.prototype.isGeometryChanged = function isGeometryChanged () {
108208           return false
108209         };
108210         MaxMidpointDistanceFilter.prototype.getMaxPointDistance = function getMaxPointDistance () {
108211           return this._maxPtDist
108212         };
108213         MaxMidpointDistanceFilter.prototype.interfaces_ = function interfaces_ () {
108214           return [CoordinateSequenceFilter]
108215         };
108216         MaxMidpointDistanceFilter.prototype.getClass = function getClass () {
108217           return MaxMidpointDistanceFilter
108218         };
108219
108220         var PolygonExtracter = function PolygonExtracter (comps) {
108221           this._comps = comps || null;
108222         };
108223         PolygonExtracter.prototype.filter = function filter (geom) {
108224           if (geom instanceof Polygon) { this._comps.add(geom); }
108225         };
108226         PolygonExtracter.prototype.interfaces_ = function interfaces_ () {
108227           return [GeometryFilter]
108228         };
108229         PolygonExtracter.prototype.getClass = function getClass () {
108230           return PolygonExtracter
108231         };
108232         PolygonExtracter.getPolygons = function getPolygons () {
108233           if (arguments.length === 1) {
108234             var geom = arguments[0];
108235             return PolygonExtracter.getPolygons(geom, new ArrayList())
108236           } else if (arguments.length === 2) {
108237             var geom$1 = arguments[0];
108238             var list = arguments[1];
108239             if (geom$1 instanceof Polygon) {
108240               list.add(geom$1);
108241             } else if (geom$1 instanceof GeometryCollection) {
108242               geom$1.apply(new PolygonExtracter(list));
108243             }
108244             return list
108245           }
108246         };
108247
108248         var LinearComponentExtracter = function LinearComponentExtracter () {
108249           this._lines = null;
108250           this._isForcedToLineString = false;
108251           if (arguments.length === 1) {
108252             var lines = arguments[0];
108253             this._lines = lines;
108254           } else if (arguments.length === 2) {
108255             var lines$1 = arguments[0];
108256             var isForcedToLineString = arguments[1];
108257             this._lines = lines$1;
108258             this._isForcedToLineString = isForcedToLineString;
108259           }
108260         };
108261         LinearComponentExtracter.prototype.filter = function filter (geom) {
108262           if (this._isForcedToLineString && geom instanceof LinearRing) {
108263             var line = geom.getFactory().createLineString(geom.getCoordinateSequence());
108264             this._lines.add(line);
108265             return null
108266           }
108267           if (geom instanceof LineString) { this._lines.add(geom); }
108268         };
108269         LinearComponentExtracter.prototype.setForceToLineString = function setForceToLineString (isForcedToLineString) {
108270           this._isForcedToLineString = isForcedToLineString;
108271         };
108272         LinearComponentExtracter.prototype.interfaces_ = function interfaces_ () {
108273           return [GeometryComponentFilter]
108274         };
108275         LinearComponentExtracter.prototype.getClass = function getClass () {
108276           return LinearComponentExtracter
108277         };
108278         LinearComponentExtracter.getGeometry = function getGeometry () {
108279           if (arguments.length === 1) {
108280             var geom = arguments[0];
108281             return geom.getFactory().buildGeometry(LinearComponentExtracter.getLines(geom))
108282           } else if (arguments.length === 2) {
108283             var geom$1 = arguments[0];
108284             var forceToLineString = arguments[1];
108285             return geom$1.getFactory().buildGeometry(LinearComponentExtracter.getLines(geom$1, forceToLineString))
108286           }
108287         };
108288         LinearComponentExtracter.getLines = function getLines () {
108289           if (arguments.length === 1) {
108290             var geom = arguments[0];
108291             return LinearComponentExtracter.getLines(geom, false)
108292           } else if (arguments.length === 2) {
108293             if (hasInterface(arguments[0], Collection) && hasInterface(arguments[1], Collection)) {
108294               var geoms = arguments[0];
108295               var lines$1 = arguments[1];
108296               for (var i = geoms.iterator(); i.hasNext();) {
108297                 var g = i.next();
108298                 LinearComponentExtracter.getLines(g, lines$1);
108299               }
108300               return lines$1
108301             } else if (arguments[0] instanceof Geometry && typeof arguments[1] === 'boolean') {
108302               var geom$1 = arguments[0];
108303               var forceToLineString = arguments[1];
108304               var lines = new ArrayList();
108305               geom$1.apply(new LinearComponentExtracter(lines, forceToLineString));
108306               return lines
108307             } else if (arguments[0] instanceof Geometry && hasInterface(arguments[1], Collection)) {
108308               var geom$2 = arguments[0];
108309               var lines$2 = arguments[1];
108310               if (geom$2 instanceof LineString) {
108311                 lines$2.add(geom$2);
108312               } else {
108313                 geom$2.apply(new LinearComponentExtracter(lines$2));
108314               }
108315               return lines$2
108316             }
108317           } else if (arguments.length === 3) {
108318             if (typeof arguments[2] === 'boolean' && (hasInterface(arguments[0], Collection) && hasInterface(arguments[1], Collection))) {
108319               var geoms$1 = arguments[0];
108320               var lines$3 = arguments[1];
108321               var forceToLineString$1 = arguments[2];
108322               for (var i$1 = geoms$1.iterator(); i$1.hasNext();) {
108323                 var g$1 = i$1.next();
108324                 LinearComponentExtracter.getLines(g$1, lines$3, forceToLineString$1);
108325               }
108326               return lines$3
108327             } else if (typeof arguments[2] === 'boolean' && (arguments[0] instanceof Geometry && hasInterface(arguments[1], Collection))) {
108328               var geom$3 = arguments[0];
108329               var lines$4 = arguments[1];
108330               var forceToLineString$2 = arguments[2];
108331               geom$3.apply(new LinearComponentExtracter(lines$4, forceToLineString$2));
108332               return lines$4
108333             }
108334           }
108335         };
108336
108337         var PointLocator = function PointLocator () {
108338           this._boundaryRule = BoundaryNodeRule.OGC_SFS_BOUNDARY_RULE;
108339           this._isIn = null;
108340           this._numBoundaries = null;
108341           if (arguments.length === 0) ; else if (arguments.length === 1) {
108342             var boundaryRule = arguments[0];
108343             if (boundaryRule === null) { throw new IllegalArgumentException('Rule must be non-null') }
108344             this._boundaryRule = boundaryRule;
108345           }
108346         };
108347         PointLocator.prototype.locateInternal = function locateInternal () {
108348             var this$1 = this;
108349
108350           if (arguments[0] instanceof Coordinate && arguments[1] instanceof Polygon) {
108351             var p = arguments[0];
108352             var poly = arguments[1];
108353             if (poly.isEmpty()) { return Location.EXTERIOR }
108354             var shell = poly.getExteriorRing();
108355             var shellLoc = this.locateInPolygonRing(p, shell);
108356             if (shellLoc === Location.EXTERIOR) { return Location.EXTERIOR }
108357             if (shellLoc === Location.BOUNDARY) { return Location.BOUNDARY }
108358             for (var i = 0; i < poly.getNumInteriorRing(); i++) {
108359               var hole = poly.getInteriorRingN(i);
108360               var holeLoc = this$1.locateInPolygonRing(p, hole);
108361               if (holeLoc === Location.INTERIOR) { return Location.EXTERIOR }
108362               if (holeLoc === Location.BOUNDARY) { return Location.BOUNDARY }
108363             }
108364             return Location.INTERIOR
108365           } else if (arguments[0] instanceof Coordinate && arguments[1] instanceof LineString) {
108366             var p$1 = arguments[0];
108367             var l = arguments[1];
108368             if (!l.getEnvelopeInternal().intersects(p$1)) { return Location.EXTERIOR }
108369             var pt = l.getCoordinates();
108370             if (!l.isClosed()) {
108371               if (p$1.equals(pt[0]) || p$1.equals(pt[pt.length - 1])) {
108372                 return Location.BOUNDARY
108373               }
108374             }
108375             if (CGAlgorithms.isOnLine(p$1, pt)) { return Location.INTERIOR }
108376             return Location.EXTERIOR
108377           } else if (arguments[0] instanceof Coordinate && arguments[1] instanceof Point$1) {
108378             var p$2 = arguments[0];
108379             var pt$1 = arguments[1];
108380             var ptCoord = pt$1.getCoordinate();
108381             if (ptCoord.equals2D(p$2)) { return Location.INTERIOR }
108382             return Location.EXTERIOR
108383           }
108384         };
108385         PointLocator.prototype.locateInPolygonRing = function locateInPolygonRing (p, ring) {
108386           if (!ring.getEnvelopeInternal().intersects(p)) { return Location.EXTERIOR }
108387           return CGAlgorithms.locatePointInRing(p, ring.getCoordinates())
108388         };
108389         PointLocator.prototype.intersects = function intersects (p, geom) {
108390           return this.locate(p, geom) !== Location.EXTERIOR
108391         };
108392         PointLocator.prototype.updateLocationInfo = function updateLocationInfo (loc) {
108393           if (loc === Location.INTERIOR) { this._isIn = true; }
108394           if (loc === Location.BOUNDARY) { this._numBoundaries++; }
108395         };
108396         PointLocator.prototype.computeLocation = function computeLocation (p, geom) {
108397             var this$1 = this;
108398
108399           if (geom instanceof Point$1) {
108400             this.updateLocationInfo(this.locateInternal(p, geom));
108401           }
108402           if (geom instanceof LineString) {
108403             this.updateLocationInfo(this.locateInternal(p, geom));
108404           } else if (geom instanceof Polygon) {
108405             this.updateLocationInfo(this.locateInternal(p, geom));
108406           } else if (geom instanceof MultiLineString) {
108407             var ml = geom;
108408             for (var i = 0; i < ml.getNumGeometries(); i++) {
108409               var l = ml.getGeometryN(i);
108410               this$1.updateLocationInfo(this$1.locateInternal(p, l));
108411             }
108412           } else if (geom instanceof MultiPolygon) {
108413             var mpoly = geom;
108414             for (var i$1 = 0; i$1 < mpoly.getNumGeometries(); i$1++) {
108415               var poly = mpoly.getGeometryN(i$1);
108416               this$1.updateLocationInfo(this$1.locateInternal(p, poly));
108417             }
108418           } else if (geom instanceof GeometryCollection) {
108419             var geomi = new GeometryCollectionIterator(geom);
108420             while (geomi.hasNext()) {
108421               var g2 = geomi.next();
108422               if (g2 !== geom) { this$1.computeLocation(p, g2); }
108423             }
108424           }
108425         };
108426         PointLocator.prototype.locate = function locate (p, geom) {
108427           if (geom.isEmpty()) { return Location.EXTERIOR }
108428           if (geom instanceof LineString) {
108429             return this.locateInternal(p, geom)
108430           } else if (geom instanceof Polygon) {
108431             return this.locateInternal(p, geom)
108432           }
108433           this._isIn = false;
108434           this._numBoundaries = 0;
108435           this.computeLocation(p, geom);
108436           if (this._boundaryRule.isInBoundary(this._numBoundaries)) { return Location.BOUNDARY }
108437           if (this._numBoundaries > 0 || this._isIn) { return Location.INTERIOR }
108438           return Location.EXTERIOR
108439         };
108440         PointLocator.prototype.interfaces_ = function interfaces_ () {
108441           return []
108442         };
108443         PointLocator.prototype.getClass = function getClass () {
108444           return PointLocator
108445         };
108446
108447         var GeometryLocation = function GeometryLocation () {
108448           this._component = null;
108449           this._segIndex = null;
108450           this._pt = null;
108451           if (arguments.length === 2) {
108452             var component = arguments[0];
108453             var pt = arguments[1];
108454             GeometryLocation.call(this, component, GeometryLocation.INSIDE_AREA, pt);
108455           } else if (arguments.length === 3) {
108456             var component$1 = arguments[0];
108457             var segIndex = arguments[1];
108458             var pt$1 = arguments[2];
108459             this._component = component$1;
108460             this._segIndex = segIndex;
108461             this._pt = pt$1;
108462           }
108463         };
108464
108465         var staticAccessors$38 = { INSIDE_AREA: { configurable: true } };
108466         GeometryLocation.prototype.isInsideArea = function isInsideArea () {
108467           return this._segIndex === GeometryLocation.INSIDE_AREA
108468         };
108469         GeometryLocation.prototype.getCoordinate = function getCoordinate () {
108470           return this._pt
108471         };
108472         GeometryLocation.prototype.getGeometryComponent = function getGeometryComponent () {
108473           return this._component
108474         };
108475         GeometryLocation.prototype.getSegmentIndex = function getSegmentIndex () {
108476           return this._segIndex
108477         };
108478         GeometryLocation.prototype.interfaces_ = function interfaces_ () {
108479           return []
108480         };
108481         GeometryLocation.prototype.getClass = function getClass () {
108482           return GeometryLocation
108483         };
108484         staticAccessors$38.INSIDE_AREA.get = function () { return -1 };
108485
108486         Object.defineProperties( GeometryLocation, staticAccessors$38 );
108487
108488         var PointExtracter = function PointExtracter (pts) {
108489           this._pts = pts || null;
108490         };
108491         PointExtracter.prototype.filter = function filter (geom) {
108492           if (geom instanceof Point$1) { this._pts.add(geom); }
108493         };
108494         PointExtracter.prototype.interfaces_ = function interfaces_ () {
108495           return [GeometryFilter]
108496         };
108497         PointExtracter.prototype.getClass = function getClass () {
108498           return PointExtracter
108499         };
108500         PointExtracter.getPoints = function getPoints () {
108501           if (arguments.length === 1) {
108502             var geom = arguments[0];
108503             if (geom instanceof Point$1) {
108504               return Collections.singletonList(geom)
108505             }
108506             return PointExtracter.getPoints(geom, new ArrayList())
108507           } else if (arguments.length === 2) {
108508             var geom$1 = arguments[0];
108509             var list = arguments[1];
108510             if (geom$1 instanceof Point$1) {
108511               list.add(geom$1);
108512             } else if (geom$1 instanceof GeometryCollection) {
108513               geom$1.apply(new PointExtracter(list));
108514             }
108515             return list
108516           }
108517         };
108518
108519         var ConnectedElementLocationFilter = function ConnectedElementLocationFilter () {
108520           this._locations = null;
108521           var locations = arguments[0];
108522           this._locations = locations;
108523         };
108524         ConnectedElementLocationFilter.prototype.filter = function filter (geom) {
108525           if (geom instanceof Point$1 || geom instanceof LineString || geom instanceof Polygon) { this._locations.add(new GeometryLocation(geom, 0, geom.getCoordinate())); }
108526         };
108527         ConnectedElementLocationFilter.prototype.interfaces_ = function interfaces_ () {
108528           return [GeometryFilter]
108529         };
108530         ConnectedElementLocationFilter.prototype.getClass = function getClass () {
108531           return ConnectedElementLocationFilter
108532         };
108533         ConnectedElementLocationFilter.getLocations = function getLocations (geom) {
108534           var locations = new ArrayList();
108535           geom.apply(new ConnectedElementLocationFilter(locations));
108536           return locations
108537         };
108538
108539         var DistanceOp = function DistanceOp () {
108540           this._geom = null;
108541           this._terminateDistance = 0.0;
108542           this._ptLocator = new PointLocator();
108543           this._minDistanceLocation = null;
108544           this._minDistance = Double.MAX_VALUE;
108545           if (arguments.length === 2) {
108546             var g0 = arguments[0];
108547             var g1 = arguments[1];
108548             this._geom = [g0, g1];
108549             this._terminateDistance = 0.0;
108550           } else if (arguments.length === 3) {
108551             var g0$1 = arguments[0];
108552             var g1$1 = arguments[1];
108553             var terminateDistance = arguments[2];
108554             this._geom = new Array(2).fill(null);
108555             this._geom[0] = g0$1;
108556             this._geom[1] = g1$1;
108557             this._terminateDistance = terminateDistance;
108558           }
108559         };
108560         DistanceOp.prototype.computeContainmentDistance = function computeContainmentDistance () {
108561             var this$1 = this;
108562
108563           if (arguments.length === 0) {
108564             var locPtPoly = new Array(2).fill(null);
108565             this.computeContainmentDistance(0, locPtPoly);
108566             if (this._minDistance <= this._terminateDistance) { return null }
108567             this.computeContainmentDistance(1, locPtPoly);
108568           } else if (arguments.length === 2) {
108569             var polyGeomIndex = arguments[0];
108570             var locPtPoly$1 = arguments[1];
108571             var locationsIndex = 1 - polyGeomIndex;
108572             var polys = PolygonExtracter.getPolygons(this._geom[polyGeomIndex]);
108573             if (polys.size() > 0) {
108574               var insideLocs = ConnectedElementLocationFilter.getLocations(this._geom[locationsIndex]);
108575               this.computeContainmentDistance(insideLocs, polys, locPtPoly$1);
108576               if (this._minDistance <= this._terminateDistance) {
108577                 this._minDistanceLocation[locationsIndex] = locPtPoly$1[0];
108578                 this._minDistanceLocation[polyGeomIndex] = locPtPoly$1[1];
108579                 return null
108580               }
108581             }
108582           } else if (arguments.length === 3) {
108583             if (arguments[2] instanceof Array && (hasInterface(arguments[0], List) && hasInterface(arguments[1], List))) {
108584               var locs = arguments[0];
108585               var polys$1 = arguments[1];
108586               var locPtPoly$2 = arguments[2];
108587               for (var i = 0; i < locs.size(); i++) {
108588                 var loc = locs.get(i);
108589                 for (var j = 0; j < polys$1.size(); j++) {
108590                   this$1.computeContainmentDistance(loc, polys$1.get(j), locPtPoly$2);
108591                   if (this$1._minDistance <= this$1._terminateDistance) { return null }
108592                 }
108593               }
108594             } else if (arguments[2] instanceof Array && (arguments[0] instanceof GeometryLocation && arguments[1] instanceof Polygon)) {
108595               var ptLoc = arguments[0];
108596               var poly = arguments[1];
108597               var locPtPoly$3 = arguments[2];
108598               var pt = ptLoc.getCoordinate();
108599               if (Location.EXTERIOR !== this._ptLocator.locate(pt, poly)) {
108600                 this._minDistance = 0.0;
108601                 locPtPoly$3[0] = ptLoc;
108602                 locPtPoly$3[1] = new GeometryLocation(poly, pt);
108603
108604                 return null
108605               }
108606             }
108607           }
108608         };
108609         DistanceOp.prototype.computeMinDistanceLinesPoints = function computeMinDistanceLinesPoints (lines, points, locGeom) {
108610             var this$1 = this;
108611
108612           for (var i = 0; i < lines.size(); i++) {
108613             var line = lines.get(i);
108614             for (var j = 0; j < points.size(); j++) {
108615               var pt = points.get(j);
108616               this$1.computeMinDistance(line, pt, locGeom);
108617               if (this$1._minDistance <= this$1._terminateDistance) { return null }
108618             }
108619           }
108620         };
108621         DistanceOp.prototype.computeFacetDistance = function computeFacetDistance () {
108622           var locGeom = new Array(2).fill(null);
108623           var lines0 = LinearComponentExtracter.getLines(this._geom[0]);
108624           var lines1 = LinearComponentExtracter.getLines(this._geom[1]);
108625           var pts0 = PointExtracter.getPoints(this._geom[0]);
108626           var pts1 = PointExtracter.getPoints(this._geom[1]);
108627           this.computeMinDistanceLines(lines0, lines1, locGeom);
108628           this.updateMinDistance(locGeom, false);
108629           if (this._minDistance <= this._terminateDistance) { return null }
108630           locGeom[0] = null;
108631           locGeom[1] = null;
108632           this.computeMinDistanceLinesPoints(lines0, pts1, locGeom);
108633           this.updateMinDistance(locGeom, false);
108634           if (this._minDistance <= this._terminateDistance) { return null }
108635           locGeom[0] = null;
108636           locGeom[1] = null;
108637           this.computeMinDistanceLinesPoints(lines1, pts0, locGeom);
108638           this.updateMinDistance(locGeom, true);
108639           if (this._minDistance <= this._terminateDistance) { return null }
108640           locGeom[0] = null;
108641           locGeom[1] = null;
108642           this.computeMinDistancePoints(pts0, pts1, locGeom);
108643           this.updateMinDistance(locGeom, false);
108644         };
108645         DistanceOp.prototype.nearestLocations = function nearestLocations () {
108646           this.computeMinDistance();
108647           return this._minDistanceLocation
108648         };
108649         DistanceOp.prototype.updateMinDistance = function updateMinDistance (locGeom, flip) {
108650           if (locGeom[0] === null) { return null }
108651           if (flip) {
108652             this._minDistanceLocation[0] = locGeom[1];
108653             this._minDistanceLocation[1] = locGeom[0];
108654           } else {
108655             this._minDistanceLocation[0] = locGeom[0];
108656             this._minDistanceLocation[1] = locGeom[1];
108657           }
108658         };
108659         DistanceOp.prototype.nearestPoints = function nearestPoints () {
108660           this.computeMinDistance();
108661           var nearestPts = [this._minDistanceLocation[0].getCoordinate(), this._minDistanceLocation[1].getCoordinate()];
108662           return nearestPts
108663         };
108664         DistanceOp.prototype.computeMinDistance = function computeMinDistance () {
108665             var this$1 = this;
108666
108667           if (arguments.length === 0) {
108668             if (this._minDistanceLocation !== null) { return null }
108669             this._minDistanceLocation = new Array(2).fill(null);
108670             this.computeContainmentDistance();
108671             if (this._minDistance <= this._terminateDistance) { return null }
108672             this.computeFacetDistance();
108673           } else if (arguments.length === 3) {
108674             if (arguments[2] instanceof Array && (arguments[0] instanceof LineString && arguments[1] instanceof Point$1)) {
108675               var line = arguments[0];
108676               var pt = arguments[1];
108677               var locGeom = arguments[2];
108678               if (line.getEnvelopeInternal().distance(pt.getEnvelopeInternal()) > this._minDistance) { return null }
108679               var coord0 = line.getCoordinates();
108680               var coord = pt.getCoordinate();
108681               for (var i = 0; i < coord0.length - 1; i++) {
108682                 var dist = CGAlgorithms.distancePointLine(coord, coord0[i], coord0[i + 1]);
108683                 if (dist < this$1._minDistance) {
108684                   this$1._minDistance = dist;
108685                   var seg = new LineSegment(coord0[i], coord0[i + 1]);
108686                   var segClosestPoint = seg.closestPoint(coord);
108687                   locGeom[0] = new GeometryLocation(line, i, segClosestPoint);
108688                   locGeom[1] = new GeometryLocation(pt, 0, coord);
108689                 }
108690                 if (this$1._minDistance <= this$1._terminateDistance) { return null }
108691               }
108692             } else if (arguments[2] instanceof Array && (arguments[0] instanceof LineString && arguments[1] instanceof LineString)) {
108693               var line0 = arguments[0];
108694               var line1 = arguments[1];
108695               var locGeom$1 = arguments[2];
108696               if (line0.getEnvelopeInternal().distance(line1.getEnvelopeInternal()) > this._minDistance) { return null }
108697               var coord0$1 = line0.getCoordinates();
108698               var coord1 = line1.getCoordinates();
108699               for (var i$1 = 0; i$1 < coord0$1.length - 1; i$1++) {
108700                 for (var j = 0; j < coord1.length - 1; j++) {
108701                   var dist$1 = CGAlgorithms.distanceLineLine(coord0$1[i$1], coord0$1[i$1 + 1], coord1[j], coord1[j + 1]);
108702                   if (dist$1 < this$1._minDistance) {
108703                     this$1._minDistance = dist$1;
108704                     var seg0 = new LineSegment(coord0$1[i$1], coord0$1[i$1 + 1]);
108705                     var seg1 = new LineSegment(coord1[j], coord1[j + 1]);
108706                     var closestPt = seg0.closestPoints(seg1);
108707                     locGeom$1[0] = new GeometryLocation(line0, i$1, closestPt[0]);
108708                     locGeom$1[1] = new GeometryLocation(line1, j, closestPt[1]);
108709                   }
108710                   if (this$1._minDistance <= this$1._terminateDistance) { return null }
108711                 }
108712               }
108713             }
108714           }
108715         };
108716         DistanceOp.prototype.computeMinDistancePoints = function computeMinDistancePoints (points0, points1, locGeom) {
108717             var this$1 = this;
108718
108719           for (var i = 0; i < points0.size(); i++) {
108720             var pt0 = points0.get(i);
108721             for (var j = 0; j < points1.size(); j++) {
108722               var pt1 = points1.get(j);
108723               var dist = pt0.getCoordinate().distance(pt1.getCoordinate());
108724               if (dist < this$1._minDistance) {
108725                 this$1._minDistance = dist;
108726                 locGeom[0] = new GeometryLocation(pt0, 0, pt0.getCoordinate());
108727                 locGeom[1] = new GeometryLocation(pt1, 0, pt1.getCoordinate());
108728               }
108729               if (this$1._minDistance <= this$1._terminateDistance) { return null }
108730             }
108731           }
108732         };
108733         DistanceOp.prototype.distance = function distance () {
108734           if (this._geom[0] === null || this._geom[1] === null) { throw new IllegalArgumentException('null geometries are not supported') }
108735           if (this._geom[0].isEmpty() || this._geom[1].isEmpty()) { return 0.0 }
108736           this.computeMinDistance();
108737           return this._minDistance
108738         };
108739         DistanceOp.prototype.computeMinDistanceLines = function computeMinDistanceLines (lines0, lines1, locGeom) {
108740             var this$1 = this;
108741
108742           for (var i = 0; i < lines0.size(); i++) {
108743             var line0 = lines0.get(i);
108744             for (var j = 0; j < lines1.size(); j++) {
108745               var line1 = lines1.get(j);
108746               this$1.computeMinDistance(line0, line1, locGeom);
108747               if (this$1._minDistance <= this$1._terminateDistance) { return null }
108748             }
108749           }
108750         };
108751         DistanceOp.prototype.interfaces_ = function interfaces_ () {
108752           return []
108753         };
108754         DistanceOp.prototype.getClass = function getClass () {
108755           return DistanceOp
108756         };
108757         DistanceOp.distance = function distance (g0, g1) {
108758           var distOp = new DistanceOp(g0, g1);
108759           return distOp.distance()
108760         };
108761         DistanceOp.isWithinDistance = function isWithinDistance (g0, g1, distance) {
108762           var distOp = new DistanceOp(g0, g1, distance);
108763           return distOp.distance() <= distance
108764         };
108765         DistanceOp.nearestPoints = function nearestPoints (g0, g1) {
108766           var distOp = new DistanceOp(g0, g1);
108767           return distOp.nearestPoints()
108768         };
108769
108770         var PointPairDistance$2 = function PointPairDistance () {
108771           this._pt = [new Coordinate(), new Coordinate()];
108772           this._distance = Double.NaN;
108773           this._isNull = true;
108774         };
108775         PointPairDistance$2.prototype.getCoordinates = function getCoordinates () {
108776           return this._pt
108777         };
108778         PointPairDistance$2.prototype.getCoordinate = function getCoordinate (i) {
108779           return this._pt[i]
108780         };
108781         PointPairDistance$2.prototype.setMinimum = function setMinimum () {
108782           if (arguments.length === 1) {
108783             var ptDist = arguments[0];
108784             this.setMinimum(ptDist._pt[0], ptDist._pt[1]);
108785           } else if (arguments.length === 2) {
108786             var p0 = arguments[0];
108787             var p1 = arguments[1];
108788             if (this._isNull) {
108789               this.initialize(p0, p1);
108790               return null
108791             }
108792             var dist = p0.distance(p1);
108793             if (dist < this._distance) { this.initialize(p0, p1, dist); }
108794           }
108795         };
108796         PointPairDistance$2.prototype.initialize = function initialize () {
108797           if (arguments.length === 0) {
108798             this._isNull = true;
108799           } else if (arguments.length === 2) {
108800             var p0 = arguments[0];
108801             var p1 = arguments[1];
108802             this._pt[0].setCoordinate(p0);
108803             this._pt[1].setCoordinate(p1);
108804             this._distance = p0.distance(p1);
108805             this._isNull = false;
108806           } else if (arguments.length === 3) {
108807             var p0$1 = arguments[0];
108808             var p1$1 = arguments[1];
108809             var distance = arguments[2];
108810             this._pt[0].setCoordinate(p0$1);
108811             this._pt[1].setCoordinate(p1$1);
108812             this._distance = distance;
108813             this._isNull = false;
108814           }
108815         };
108816         PointPairDistance$2.prototype.toString = function toString () {
108817           return WKTWriter.toLineString(this._pt[0], this._pt[1])
108818         };
108819         PointPairDistance$2.prototype.getDistance = function getDistance () {
108820           return this._distance
108821         };
108822         PointPairDistance$2.prototype.setMaximum = function setMaximum () {
108823           if (arguments.length === 1) {
108824             var ptDist = arguments[0];
108825             this.setMaximum(ptDist._pt[0], ptDist._pt[1]);
108826           } else if (arguments.length === 2) {
108827             var p0 = arguments[0];
108828             var p1 = arguments[1];
108829             if (this._isNull) {
108830               this.initialize(p0, p1);
108831               return null
108832             }
108833             var dist = p0.distance(p1);
108834             if (dist > this._distance) { this.initialize(p0, p1, dist); }
108835           }
108836         };
108837         PointPairDistance$2.prototype.interfaces_ = function interfaces_ () {
108838           return []
108839         };
108840         PointPairDistance$2.prototype.getClass = function getClass () {
108841           return PointPairDistance$2
108842         };
108843
108844         var DistanceToPoint = function DistanceToPoint () {};
108845
108846         DistanceToPoint.prototype.interfaces_ = function interfaces_ () {
108847           return []
108848         };
108849         DistanceToPoint.prototype.getClass = function getClass () {
108850           return DistanceToPoint
108851         };
108852         DistanceToPoint.computeDistance = function computeDistance () {
108853           if (arguments[2] instanceof PointPairDistance$2 && (arguments[0] instanceof LineString && arguments[1] instanceof Coordinate)) {
108854             var line = arguments[0];
108855             var pt = arguments[1];
108856             var ptDist = arguments[2];
108857             var tempSegment = new LineSegment();
108858             var coords = line.getCoordinates();
108859             for (var i = 0; i < coords.length - 1; i++) {
108860               tempSegment.setCoordinates(coords[i], coords[i + 1]);
108861               var closestPt = tempSegment.closestPoint(pt);
108862               ptDist.setMinimum(closestPt, pt);
108863             }
108864           } else if (arguments[2] instanceof PointPairDistance$2 && (arguments[0] instanceof Polygon && arguments[1] instanceof Coordinate)) {
108865             var poly = arguments[0];
108866             var pt$1 = arguments[1];
108867             var ptDist$1 = arguments[2];
108868             DistanceToPoint.computeDistance(poly.getExteriorRing(), pt$1, ptDist$1);
108869             for (var i$1 = 0; i$1 < poly.getNumInteriorRing(); i$1++) {
108870               DistanceToPoint.computeDistance(poly.getInteriorRingN(i$1), pt$1, ptDist$1);
108871             }
108872           } else if (arguments[2] instanceof PointPairDistance$2 && (arguments[0] instanceof Geometry && arguments[1] instanceof Coordinate)) {
108873             var geom = arguments[0];
108874             var pt$2 = arguments[1];
108875             var ptDist$2 = arguments[2];
108876             if (geom instanceof LineString) {
108877               DistanceToPoint.computeDistance(geom, pt$2, ptDist$2);
108878             } else if (geom instanceof Polygon) {
108879               DistanceToPoint.computeDistance(geom, pt$2, ptDist$2);
108880             } else if (geom instanceof GeometryCollection) {
108881               var gc = geom;
108882               for (var i$2 = 0; i$2 < gc.getNumGeometries(); i$2++) {
108883                 var g = gc.getGeometryN(i$2);
108884                 DistanceToPoint.computeDistance(g, pt$2, ptDist$2);
108885               }
108886             } else {
108887               ptDist$2.setMinimum(geom.getCoordinate(), pt$2);
108888             }
108889           } else if (arguments[2] instanceof PointPairDistance$2 && (arguments[0] instanceof LineSegment && arguments[1] instanceof Coordinate)) {
108890             var segment = arguments[0];
108891             var pt$3 = arguments[1];
108892             var ptDist$3 = arguments[2];
108893             var closestPt$1 = segment.closestPoint(pt$3);
108894             ptDist$3.setMinimum(closestPt$1, pt$3);
108895           }
108896         };
108897
108898         var DiscreteHausdorffDistance = function DiscreteHausdorffDistance () {
108899           this._g0 = null;
108900           this._g1 = null;
108901           this._ptDist = new PointPairDistance$2();
108902           this._densifyFrac = 0.0;
108903           var g0 = arguments[0];
108904           var g1 = arguments[1];
108905           this._g0 = g0;
108906           this._g1 = g1;
108907         };
108908
108909         var staticAccessors$39 = { MaxPointDistanceFilter: { configurable: true },MaxDensifiedByFractionDistanceFilter: { configurable: true } };
108910         DiscreteHausdorffDistance.prototype.getCoordinates = function getCoordinates () {
108911           return this._ptDist.getCoordinates()
108912         };
108913         DiscreteHausdorffDistance.prototype.setDensifyFraction = function setDensifyFraction (densifyFrac) {
108914           if (densifyFrac > 1.0 || densifyFrac <= 0.0) { throw new IllegalArgumentException('Fraction is not in range (0.0 - 1.0]') }
108915           this._densifyFrac = densifyFrac;
108916         };
108917         DiscreteHausdorffDistance.prototype.compute = function compute (g0, g1) {
108918           this.computeOrientedDistance(g0, g1, this._ptDist);
108919           this.computeOrientedDistance(g1, g0, this._ptDist);
108920         };
108921         DiscreteHausdorffDistance.prototype.distance = function distance () {
108922           this.compute(this._g0, this._g1);
108923           return this._ptDist.getDistance()
108924         };
108925         DiscreteHausdorffDistance.prototype.computeOrientedDistance = function computeOrientedDistance (discreteGeom, geom, ptDist) {
108926           var distFilter = new MaxPointDistanceFilter$1(geom);
108927           discreteGeom.apply(distFilter);
108928           ptDist.setMaximum(distFilter.getMaxPointDistance());
108929           if (this._densifyFrac > 0) {
108930             var fracFilter = new MaxDensifiedByFractionDistanceFilter(geom, this._densifyFrac);
108931             discreteGeom.apply(fracFilter);
108932             ptDist.setMaximum(fracFilter.getMaxPointDistance());
108933           }
108934         };
108935         DiscreteHausdorffDistance.prototype.orientedDistance = function orientedDistance () {
108936           this.computeOrientedDistance(this._g0, this._g1, this._ptDist);
108937           return this._ptDist.getDistance()
108938         };
108939         DiscreteHausdorffDistance.prototype.interfaces_ = function interfaces_ () {
108940           return []
108941         };
108942         DiscreteHausdorffDistance.prototype.getClass = function getClass () {
108943           return DiscreteHausdorffDistance
108944         };
108945         DiscreteHausdorffDistance.distance = function distance () {
108946           if (arguments.length === 2) {
108947             var g0 = arguments[0];
108948             var g1 = arguments[1];
108949             var dist = new DiscreteHausdorffDistance(g0, g1);
108950             return dist.distance()
108951           } else if (arguments.length === 3) {
108952             var g0$1 = arguments[0];
108953             var g1$1 = arguments[1];
108954             var densifyFrac = arguments[2];
108955             var dist$1 = new DiscreteHausdorffDistance(g0$1, g1$1);
108956             dist$1.setDensifyFraction(densifyFrac);
108957             return dist$1.distance()
108958           }
108959         };
108960         staticAccessors$39.MaxPointDistanceFilter.get = function () { return MaxPointDistanceFilter$1 };
108961         staticAccessors$39.MaxDensifiedByFractionDistanceFilter.get = function () { return MaxDensifiedByFractionDistanceFilter };
108962
108963         Object.defineProperties( DiscreteHausdorffDistance, staticAccessors$39 );
108964
108965         var MaxPointDistanceFilter$1 = function MaxPointDistanceFilter () {
108966           this._maxPtDist = new PointPairDistance$2();
108967           this._minPtDist = new PointPairDistance$2();
108968           this._euclideanDist = new DistanceToPoint();
108969           this._geom = null;
108970           var geom = arguments[0];
108971           this._geom = geom;
108972         };
108973         MaxPointDistanceFilter$1.prototype.filter = function filter (pt) {
108974           this._minPtDist.initialize();
108975           DistanceToPoint.computeDistance(this._geom, pt, this._minPtDist);
108976           this._maxPtDist.setMaximum(this._minPtDist);
108977         };
108978         MaxPointDistanceFilter$1.prototype.getMaxPointDistance = function getMaxPointDistance () {
108979           return this._maxPtDist
108980         };
108981         MaxPointDistanceFilter$1.prototype.interfaces_ = function interfaces_ () {
108982           return [CoordinateFilter]
108983         };
108984         MaxPointDistanceFilter$1.prototype.getClass = function getClass () {
108985           return MaxPointDistanceFilter$1
108986         };
108987
108988         var MaxDensifiedByFractionDistanceFilter = function MaxDensifiedByFractionDistanceFilter () {
108989           this._maxPtDist = new PointPairDistance$2();
108990           this._minPtDist = new PointPairDistance$2();
108991           this._geom = null;
108992           this._numSubSegs = 0;
108993           var geom = arguments[0];
108994           var fraction = arguments[1];
108995           this._geom = geom;
108996           this._numSubSegs = Math.trunc(Math.round(1.0 / fraction));
108997         };
108998         MaxDensifiedByFractionDistanceFilter.prototype.filter = function filter (seq, index) {
108999             var this$1 = this;
109000
109001           if (index === 0) { return null }
109002           var p0 = seq.getCoordinate(index - 1);
109003           var p1 = seq.getCoordinate(index);
109004           var delx = (p1.x - p0.x) / this._numSubSegs;
109005           var dely = (p1.y - p0.y) / this._numSubSegs;
109006           for (var i = 0; i < this._numSubSegs; i++) {
109007             var x = p0.x + i * delx;
109008             var y = p0.y + i * dely;
109009             var pt = new Coordinate(x, y);
109010             this$1._minPtDist.initialize();
109011             DistanceToPoint.computeDistance(this$1._geom, pt, this$1._minPtDist);
109012             this$1._maxPtDist.setMaximum(this$1._minPtDist);
109013           }
109014         };
109015         MaxDensifiedByFractionDistanceFilter.prototype.isDone = function isDone () {
109016           return false
109017         };
109018         MaxDensifiedByFractionDistanceFilter.prototype.isGeometryChanged = function isGeometryChanged () {
109019           return false
109020         };
109021         MaxDensifiedByFractionDistanceFilter.prototype.getMaxPointDistance = function getMaxPointDistance () {
109022           return this._maxPtDist
109023         };
109024         MaxDensifiedByFractionDistanceFilter.prototype.interfaces_ = function interfaces_ () {
109025           return [CoordinateSequenceFilter]
109026         };
109027         MaxDensifiedByFractionDistanceFilter.prototype.getClass = function getClass () {
109028           return MaxDensifiedByFractionDistanceFilter
109029         };
109030
109031         var BufferDistanceValidator = function BufferDistanceValidator (input, bufDistance, result) {
109032           this._minValidDistance = null;
109033           this._maxValidDistance = null;
109034           this._minDistanceFound = null;
109035           this._maxDistanceFound = null;
109036           this._isValid = true;
109037           this._errMsg = null;
109038           this._errorLocation = null;
109039           this._errorIndicator = null;
109040           this._input = input || null;
109041           this._bufDistance = bufDistance || null;
109042           this._result = result || null;
109043         };
109044
109045         var staticAccessors$37 = { VERBOSE: { configurable: true },MAX_DISTANCE_DIFF_FRAC: { configurable: true } };
109046         BufferDistanceValidator.prototype.checkMaximumDistance = function checkMaximumDistance (input, bufCurve, maxDist) {
109047           var haus = new DiscreteHausdorffDistance(bufCurve, input);
109048           haus.setDensifyFraction(0.25);
109049           this._maxDistanceFound = haus.orientedDistance();
109050           if (this._maxDistanceFound > maxDist) {
109051             this._isValid = false;
109052             var pts = haus.getCoordinates();
109053             this._errorLocation = pts[1];
109054             this._errorIndicator = input.getFactory().createLineString(pts);
109055             this._errMsg = 'Distance between buffer curve and input is too large (' + this._maxDistanceFound + ' at ' + WKTWriter.toLineString(pts[0], pts[1]) + ')';
109056           }
109057         };
109058         BufferDistanceValidator.prototype.isValid = function isValid () {
109059           var posDistance = Math.abs(this._bufDistance);
109060           var distDelta = BufferDistanceValidator.MAX_DISTANCE_DIFF_FRAC * posDistance;
109061           this._minValidDistance = posDistance - distDelta;
109062           this._maxValidDistance = posDistance + distDelta;
109063           if (this._input.isEmpty() || this._result.isEmpty()) { return true }
109064           if (this._bufDistance > 0.0) {
109065             this.checkPositiveValid();
109066           } else {
109067             this.checkNegativeValid();
109068           }
109069           if (BufferDistanceValidator.VERBOSE) {
109070             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));
109071           }
109072           return this._isValid
109073         };
109074         BufferDistanceValidator.prototype.checkNegativeValid = function checkNegativeValid () {
109075           if (!(this._input instanceof Polygon || this._input instanceof MultiPolygon || this._input instanceof GeometryCollection)) {
109076             return null
109077           }
109078           var inputCurve = this.getPolygonLines(this._input);
109079           this.checkMinimumDistance(inputCurve, this._result, this._minValidDistance);
109080           if (!this._isValid) { return null }
109081           this.checkMaximumDistance(inputCurve, this._result, this._maxValidDistance);
109082         };
109083         BufferDistanceValidator.prototype.getErrorIndicator = function getErrorIndicator () {
109084           return this._errorIndicator
109085         };
109086         BufferDistanceValidator.prototype.checkMinimumDistance = function checkMinimumDistance (g1, g2, minDist) {
109087           var distOp = new DistanceOp(g1, g2, minDist);
109088           this._minDistanceFound = distOp.distance();
109089           if (this._minDistanceFound < minDist) {
109090             this._isValid = false;
109091             var pts = distOp.nearestPoints();
109092             this._errorLocation = distOp.nearestPoints()[1];
109093             this._errorIndicator = g1.getFactory().createLineString(pts);
109094             this._errMsg = 'Distance between buffer curve and input is too small (' + this._minDistanceFound + ' at ' + WKTWriter.toLineString(pts[0], pts[1]) + ' )';
109095           }
109096         };
109097         BufferDistanceValidator.prototype.checkPositiveValid = function checkPositiveValid () {
109098           var bufCurve = this._result.getBoundary();
109099           this.checkMinimumDistance(this._input, bufCurve, this._minValidDistance);
109100           if (!this._isValid) { return null }
109101           this.checkMaximumDistance(this._input, bufCurve, this._maxValidDistance);
109102         };
109103         BufferDistanceValidator.prototype.getErrorLocation = function getErrorLocation () {
109104           return this._errorLocation
109105         };
109106         BufferDistanceValidator.prototype.getPolygonLines = function getPolygonLines (g) {
109107           var lines = new ArrayList();
109108           var lineExtracter = new LinearComponentExtracter(lines);
109109           var polys = PolygonExtracter.getPolygons(g);
109110           for (var i = polys.iterator(); i.hasNext();) {
109111             var poly = i.next();
109112             poly.apply(lineExtracter);
109113           }
109114           return g.getFactory().buildGeometry(lines)
109115         };
109116         BufferDistanceValidator.prototype.getErrorMessage = function getErrorMessage () {
109117           return this._errMsg
109118         };
109119         BufferDistanceValidator.prototype.interfaces_ = function interfaces_ () {
109120           return []
109121         };
109122         BufferDistanceValidator.prototype.getClass = function getClass () {
109123           return BufferDistanceValidator
109124         };
109125         staticAccessors$37.VERBOSE.get = function () { return false };
109126         staticAccessors$37.MAX_DISTANCE_DIFF_FRAC.get = function () { return 0.012 };
109127
109128         Object.defineProperties( BufferDistanceValidator, staticAccessors$37 );
109129
109130         var BufferResultValidator = function BufferResultValidator (input, distance, result) {
109131           this._isValid = true;
109132           this._errorMsg = null;
109133           this._errorLocation = null;
109134           this._errorIndicator = null;
109135           this._input = input || null;
109136           this._distance = distance || null;
109137           this._result = result || null;
109138         };
109139
109140         var staticAccessors$40 = { VERBOSE: { configurable: true },MAX_ENV_DIFF_FRAC: { configurable: true } };
109141         BufferResultValidator.prototype.isValid = function isValid () {
109142           this.checkPolygonal();
109143           if (!this._isValid) { return this._isValid }
109144           this.checkExpectedEmpty();
109145           if (!this._isValid) { return this._isValid }
109146           this.checkEnvelope();
109147           if (!this._isValid) { return this._isValid }
109148           this.checkArea();
109149           if (!this._isValid) { return this._isValid }
109150           this.checkDistance();
109151           return this._isValid
109152         };
109153         BufferResultValidator.prototype.checkEnvelope = function checkEnvelope () {
109154           if (this._distance < 0.0) { return null }
109155           var padding = this._distance * BufferResultValidator.MAX_ENV_DIFF_FRAC;
109156           if (padding === 0.0) { padding = 0.001; }
109157           var expectedEnv = new Envelope(this._input.getEnvelopeInternal());
109158           expectedEnv.expandBy(this._distance);
109159           var bufEnv = new Envelope(this._result.getEnvelopeInternal());
109160           bufEnv.expandBy(padding);
109161           if (!bufEnv.contains(expectedEnv)) {
109162             this._isValid = false;
109163             this._errorMsg = 'Buffer envelope is incorrect';
109164             this._errorIndicator = this._input.getFactory().toGeometry(bufEnv);
109165           }
109166           this.report('Envelope');
109167         };
109168         BufferResultValidator.prototype.checkDistance = function checkDistance () {
109169           var distValid = new BufferDistanceValidator(this._input, this._distance, this._result);
109170           if (!distValid.isValid()) {
109171             this._isValid = false;
109172             this._errorMsg = distValid.getErrorMessage();
109173             this._errorLocation = distValid.getErrorLocation();
109174             this._errorIndicator = distValid.getErrorIndicator();
109175           }
109176           this.report('Distance');
109177         };
109178         BufferResultValidator.prototype.checkArea = function checkArea () {
109179           var inputArea = this._input.getArea();
109180           var resultArea = this._result.getArea();
109181           if (this._distance > 0.0 && inputArea > resultArea) {
109182             this._isValid = false;
109183             this._errorMsg = 'Area of positive buffer is smaller than input';
109184             this._errorIndicator = this._result;
109185           }
109186           if (this._distance < 0.0 && inputArea < resultArea) {
109187             this._isValid = false;
109188             this._errorMsg = 'Area of negative buffer is larger than input';
109189             this._errorIndicator = this._result;
109190           }
109191           this.report('Area');
109192         };
109193         BufferResultValidator.prototype.checkPolygonal = function checkPolygonal () {
109194           if (!(this._result instanceof Polygon || this._result instanceof MultiPolygon)) { this._isValid = false; }
109195           this._errorMsg = 'Result is not polygonal';
109196           this._errorIndicator = this._result;
109197           this.report('Polygonal');
109198         };
109199         BufferResultValidator.prototype.getErrorIndicator = function getErrorIndicator () {
109200           return this._errorIndicator
109201         };
109202         BufferResultValidator.prototype.getErrorLocation = function getErrorLocation () {
109203           return this._errorLocation
109204         };
109205         BufferResultValidator.prototype.checkExpectedEmpty = function checkExpectedEmpty () {
109206           if (this._input.getDimension() >= 2) { return null }
109207           if (this._distance > 0.0) { return null }
109208           if (!this._result.isEmpty()) {
109209             this._isValid = false;
109210             this._errorMsg = 'Result is non-empty';
109211             this._errorIndicator = this._result;
109212           }
109213           this.report('ExpectedEmpty');
109214         };
109215         BufferResultValidator.prototype.report = function report (checkName) {
109216           if (!BufferResultValidator.VERBOSE) { return null }
109217           System.out.println('Check ' + checkName + ': ' + (this._isValid ? 'passed' : 'FAILED'));
109218         };
109219         BufferResultValidator.prototype.getErrorMessage = function getErrorMessage () {
109220           return this._errorMsg
109221         };
109222         BufferResultValidator.prototype.interfaces_ = function interfaces_ () {
109223           return []
109224         };
109225         BufferResultValidator.prototype.getClass = function getClass () {
109226           return BufferResultValidator
109227         };
109228         BufferResultValidator.isValidMsg = function isValidMsg (g, distance, result) {
109229           var validator = new BufferResultValidator(g, distance, result);
109230           if (!validator.isValid()) { return validator.getErrorMessage() }
109231           return null
109232         };
109233         BufferResultValidator.isValid = function isValid (g, distance, result) {
109234           var validator = new BufferResultValidator(g, distance, result);
109235           if (validator.isValid()) { return true }
109236           return false
109237         };
109238         staticAccessors$40.VERBOSE.get = function () { return false };
109239         staticAccessors$40.MAX_ENV_DIFF_FRAC.get = function () { return 0.012 };
109240
109241         Object.defineProperties( BufferResultValidator, staticAccessors$40 );
109242
109243         // operation.buffer
109244
109245         var BasicSegmentString = function BasicSegmentString () {
109246           this._pts = null;
109247           this._data = null;
109248           var pts = arguments[0];
109249           var data = arguments[1];
109250           this._pts = pts;
109251           this._data = data;
109252         };
109253         BasicSegmentString.prototype.getCoordinates = function getCoordinates () {
109254           return this._pts
109255         };
109256         BasicSegmentString.prototype.size = function size () {
109257           return this._pts.length
109258         };
109259         BasicSegmentString.prototype.getCoordinate = function getCoordinate (i) {
109260           return this._pts[i]
109261         };
109262         BasicSegmentString.prototype.isClosed = function isClosed () {
109263           return this._pts[0].equals(this._pts[this._pts.length - 1])
109264         };
109265         BasicSegmentString.prototype.getSegmentOctant = function getSegmentOctant (index) {
109266           if (index === this._pts.length - 1) { return -1 }
109267           return Octant.octant(this.getCoordinate(index), this.getCoordinate(index + 1))
109268         };
109269         BasicSegmentString.prototype.setData = function setData (data) {
109270           this._data = data;
109271         };
109272         BasicSegmentString.prototype.getData = function getData () {
109273           return this._data
109274         };
109275         BasicSegmentString.prototype.toString = function toString () {
109276           return WKTWriter.toLineString(new CoordinateArraySequence(this._pts))
109277         };
109278         BasicSegmentString.prototype.interfaces_ = function interfaces_ () {
109279           return [SegmentString]
109280         };
109281         BasicSegmentString.prototype.getClass = function getClass () {
109282           return BasicSegmentString
109283         };
109284
109285         var InteriorIntersectionFinder = function InteriorIntersectionFinder () {
109286           this._findAllIntersections = false;
109287           this._isCheckEndSegmentsOnly = false;
109288           this._li = null;
109289           this._interiorIntersection = null;
109290           this._intSegments = null;
109291           this._intersections = new ArrayList();
109292           this._intersectionCount = 0;
109293           this._keepIntersections = true;
109294           var li = arguments[0];
109295           this._li = li;
109296           this._interiorIntersection = null;
109297         };
109298         InteriorIntersectionFinder.prototype.getInteriorIntersection = function getInteriorIntersection () {
109299           return this._interiorIntersection
109300         };
109301         InteriorIntersectionFinder.prototype.setCheckEndSegmentsOnly = function setCheckEndSegmentsOnly (isCheckEndSegmentsOnly) {
109302           this._isCheckEndSegmentsOnly = isCheckEndSegmentsOnly;
109303         };
109304         InteriorIntersectionFinder.prototype.getIntersectionSegments = function getIntersectionSegments () {
109305           return this._intSegments
109306         };
109307         InteriorIntersectionFinder.prototype.count = function count () {
109308           return this._intersectionCount
109309         };
109310         InteriorIntersectionFinder.prototype.getIntersections = function getIntersections () {
109311           return this._intersections
109312         };
109313         InteriorIntersectionFinder.prototype.setFindAllIntersections = function setFindAllIntersections (findAllIntersections) {
109314           this._findAllIntersections = findAllIntersections;
109315         };
109316         InteriorIntersectionFinder.prototype.setKeepIntersections = function setKeepIntersections (keepIntersections) {
109317           this._keepIntersections = keepIntersections;
109318         };
109319         InteriorIntersectionFinder.prototype.processIntersections = function processIntersections (e0, segIndex0, e1, segIndex1) {
109320           if (!this._findAllIntersections && this.hasIntersection()) { return null }
109321           if (e0 === e1 && segIndex0 === segIndex1) { return null }
109322           if (this._isCheckEndSegmentsOnly) {
109323             var isEndSegPresent = this.isEndSegment(e0, segIndex0) || this.isEndSegment(e1, segIndex1);
109324             if (!isEndSegPresent) { return null }
109325           }
109326           var p00 = e0.getCoordinates()[segIndex0];
109327           var p01 = e0.getCoordinates()[segIndex0 + 1];
109328           var p10 = e1.getCoordinates()[segIndex1];
109329           var p11 = e1.getCoordinates()[segIndex1 + 1];
109330           this._li.computeIntersection(p00, p01, p10, p11);
109331           if (this._li.hasIntersection()) {
109332             if (this._li.isInteriorIntersection()) {
109333               this._intSegments = new Array(4).fill(null);
109334               this._intSegments[0] = p00;
109335               this._intSegments[1] = p01;
109336               this._intSegments[2] = p10;
109337               this._intSegments[3] = p11;
109338               this._interiorIntersection = this._li.getIntersection(0);
109339               if (this._keepIntersections) { this._intersections.add(this._interiorIntersection); }
109340               this._intersectionCount++;
109341             }
109342           }
109343         };
109344         InteriorIntersectionFinder.prototype.isEndSegment = function isEndSegment (segStr, index) {
109345           if (index === 0) { return true }
109346           if (index >= segStr.size() - 2) { return true }
109347           return false
109348         };
109349         InteriorIntersectionFinder.prototype.hasIntersection = function hasIntersection () {
109350           return this._interiorIntersection !== null
109351         };
109352         InteriorIntersectionFinder.prototype.isDone = function isDone () {
109353           if (this._findAllIntersections) { return false }
109354           return this._interiorIntersection !== null
109355         };
109356         InteriorIntersectionFinder.prototype.interfaces_ = function interfaces_ () {
109357           return [SegmentIntersector]
109358         };
109359         InteriorIntersectionFinder.prototype.getClass = function getClass () {
109360           return InteriorIntersectionFinder
109361         };
109362         InteriorIntersectionFinder.createAllIntersectionsFinder = function createAllIntersectionsFinder (li) {
109363           var finder = new InteriorIntersectionFinder(li);
109364           finder.setFindAllIntersections(true);
109365           return finder
109366         };
109367         InteriorIntersectionFinder.createAnyIntersectionFinder = function createAnyIntersectionFinder (li) {
109368           return new InteriorIntersectionFinder(li)
109369         };
109370         InteriorIntersectionFinder.createIntersectionCounter = function createIntersectionCounter (li) {
109371           var finder = new InteriorIntersectionFinder(li);
109372           finder.setFindAllIntersections(true);
109373           finder.setKeepIntersections(false);
109374           return finder
109375         };
109376
109377         var FastNodingValidator = function FastNodingValidator () {
109378           this._li = new RobustLineIntersector();
109379           this._segStrings = null;
109380           this._findAllIntersections = false;
109381           this._segInt = null;
109382           this._isValid = true;
109383           var segStrings = arguments[0];
109384           this._segStrings = segStrings;
109385         };
109386         FastNodingValidator.prototype.execute = function execute () {
109387           if (this._segInt !== null) { return null }
109388           this.checkInteriorIntersections();
109389         };
109390         FastNodingValidator.prototype.getIntersections = function getIntersections () {
109391           return this._segInt.getIntersections()
109392         };
109393         FastNodingValidator.prototype.isValid = function isValid () {
109394           this.execute();
109395           return this._isValid
109396         };
109397         FastNodingValidator.prototype.setFindAllIntersections = function setFindAllIntersections (findAllIntersections) {
109398           this._findAllIntersections = findAllIntersections;
109399         };
109400         FastNodingValidator.prototype.checkInteriorIntersections = function checkInteriorIntersections () {
109401           this._isValid = true;
109402           this._segInt = new InteriorIntersectionFinder(this._li);
109403           this._segInt.setFindAllIntersections(this._findAllIntersections);
109404           var noder = new MCIndexNoder();
109405           noder.setSegmentIntersector(this._segInt);
109406           noder.computeNodes(this._segStrings);
109407           if (this._segInt.hasIntersection()) {
109408             this._isValid = false;
109409             return null
109410           }
109411         };
109412         FastNodingValidator.prototype.checkValid = function checkValid () {
109413           this.execute();
109414           if (!this._isValid) { throw new TopologyException(this.getErrorMessage(), this._segInt.getInteriorIntersection()) }
109415         };
109416         FastNodingValidator.prototype.getErrorMessage = function getErrorMessage () {
109417           if (this._isValid) { return 'no intersections found' }
109418           var intSegs = this._segInt.getIntersectionSegments();
109419           return 'found non-noded intersection between ' + WKTWriter.toLineString(intSegs[0], intSegs[1]) + ' and ' + WKTWriter.toLineString(intSegs[2], intSegs[3])
109420         };
109421         FastNodingValidator.prototype.interfaces_ = function interfaces_ () {
109422           return []
109423         };
109424         FastNodingValidator.prototype.getClass = function getClass () {
109425           return FastNodingValidator
109426         };
109427         FastNodingValidator.computeIntersections = function computeIntersections (segStrings) {
109428           var nv = new FastNodingValidator(segStrings);
109429           nv.setFindAllIntersections(true);
109430           nv.isValid();
109431           return nv.getIntersections()
109432         };
109433
109434         var EdgeNodingValidator = function EdgeNodingValidator () {
109435           this._nv = null;
109436           var edges = arguments[0];
109437           this._nv = new FastNodingValidator(EdgeNodingValidator.toSegmentStrings(edges));
109438         };
109439         EdgeNodingValidator.prototype.checkValid = function checkValid () {
109440           this._nv.checkValid();
109441         };
109442         EdgeNodingValidator.prototype.interfaces_ = function interfaces_ () {
109443           return []
109444         };
109445         EdgeNodingValidator.prototype.getClass = function getClass () {
109446           return EdgeNodingValidator
109447         };
109448         EdgeNodingValidator.toSegmentStrings = function toSegmentStrings (edges) {
109449           var segStrings = new ArrayList();
109450           for (var i = edges.iterator(); i.hasNext();) {
109451             var e = i.next();
109452             segStrings.add(new BasicSegmentString(e.getCoordinates(), e));
109453           }
109454           return segStrings
109455         };
109456         EdgeNodingValidator.checkValid = function checkValid (edges) {
109457           var validator = new EdgeNodingValidator(edges);
109458           validator.checkValid();
109459         };
109460
109461         var GeometryCollectionMapper = function GeometryCollectionMapper (mapOp) {
109462           this._mapOp = mapOp;
109463         };
109464         GeometryCollectionMapper.prototype.map = function map (gc) {
109465             var this$1 = this;
109466
109467           var mapped = new ArrayList();
109468           for (var i = 0; i < gc.getNumGeometries(); i++) {
109469             var g = this$1._mapOp.map(gc.getGeometryN(i));
109470             if (!g.isEmpty()) { mapped.add(g); }
109471           }
109472           return gc.getFactory().createGeometryCollection(GeometryFactory.toGeometryArray(mapped))
109473         };
109474         GeometryCollectionMapper.prototype.interfaces_ = function interfaces_ () {
109475           return []
109476         };
109477         GeometryCollectionMapper.prototype.getClass = function getClass () {
109478           return GeometryCollectionMapper
109479         };
109480         GeometryCollectionMapper.map = function map (gc, op) {
109481           var mapper = new GeometryCollectionMapper(op);
109482           return mapper.map(gc)
109483         };
109484
109485         var LineBuilder = function LineBuilder () {
109486           this._op = null;
109487           this._geometryFactory = null;
109488           this._ptLocator = null;
109489           this._lineEdgesList = new ArrayList();
109490           this._resultLineList = new ArrayList();
109491           var op = arguments[0];
109492           var geometryFactory = arguments[1];
109493           var ptLocator = arguments[2];
109494           this._op = op;
109495           this._geometryFactory = geometryFactory;
109496           this._ptLocator = ptLocator;
109497         };
109498         LineBuilder.prototype.collectLines = function collectLines (opCode) {
109499             var this$1 = this;
109500
109501           for (var it = this._op.getGraph().getEdgeEnds().iterator(); it.hasNext();) {
109502             var de = it.next();
109503             this$1.collectLineEdge(de, opCode, this$1._lineEdgesList);
109504             this$1.collectBoundaryTouchEdge(de, opCode, this$1._lineEdgesList);
109505           }
109506         };
109507         LineBuilder.prototype.labelIsolatedLine = function labelIsolatedLine (e, targetIndex) {
109508           var loc = this._ptLocator.locate(e.getCoordinate(), this._op.getArgGeometry(targetIndex));
109509           e.getLabel().setLocation(targetIndex, loc);
109510         };
109511         LineBuilder.prototype.build = function build (opCode) {
109512           this.findCoveredLineEdges();
109513           this.collectLines(opCode);
109514           this.buildLines(opCode);
109515           return this._resultLineList
109516         };
109517         LineBuilder.prototype.collectLineEdge = function collectLineEdge (de, opCode, edges) {
109518           var label = de.getLabel();
109519           var e = de.getEdge();
109520           if (de.isLineEdge()) {
109521             if (!de.isVisited() && OverlayOp.isResultOfOp(label, opCode) && !e.isCovered()) {
109522               edges.add(e);
109523               de.setVisitedEdge(true);
109524             }
109525           }
109526         };
109527         LineBuilder.prototype.findCoveredLineEdges = function findCoveredLineEdges () {
109528             var this$1 = this;
109529
109530           for (var nodeit = this._op.getGraph().getNodes().iterator(); nodeit.hasNext();) {
109531             var node = nodeit.next();
109532             node.getEdges().findCoveredLineEdges();
109533           }
109534           for (var it = this._op.getGraph().getEdgeEnds().iterator(); it.hasNext();) {
109535             var de = it.next();
109536             var e = de.getEdge();
109537             if (de.isLineEdge() && !e.isCoveredSet()) {
109538               var isCovered = this$1._op.isCoveredByA(de.getCoordinate());
109539               e.setCovered(isCovered);
109540             }
109541           }
109542         };
109543         LineBuilder.prototype.labelIsolatedLines = function labelIsolatedLines (edgesList) {
109544             var this$1 = this;
109545
109546           for (var it = edgesList.iterator(); it.hasNext();) {
109547             var e = it.next();
109548             var label = e.getLabel();
109549             if (e.isIsolated()) {
109550               if (label.isNull(0)) { this$1.labelIsolatedLine(e, 0); } else { this$1.labelIsolatedLine(e, 1); }
109551             }
109552           }
109553         };
109554         LineBuilder.prototype.buildLines = function buildLines (opCode) {
109555             var this$1 = this;
109556
109557           for (var it = this._lineEdgesList.iterator(); it.hasNext();) {
109558             var e = it.next();
109559             // const label = e.getLabel()
109560             var line = this$1._geometryFactory.createLineString(e.getCoordinates());
109561             this$1._resultLineList.add(line);
109562             e.setInResult(true);
109563           }
109564         };
109565         LineBuilder.prototype.collectBoundaryTouchEdge = function collectBoundaryTouchEdge (de, opCode, edges) {
109566           var label = de.getLabel();
109567           if (de.isLineEdge()) { return null }
109568           if (de.isVisited()) { return null }
109569           if (de.isInteriorAreaEdge()) { return null }
109570           if (de.getEdge().isInResult()) { return null }
109571           Assert.isTrue(!(de.isInResult() || de.getSym().isInResult()) || !de.getEdge().isInResult());
109572           if (OverlayOp.isResultOfOp(label, opCode) && opCode === OverlayOp.INTERSECTION) {
109573             edges.add(de.getEdge());
109574             de.setVisitedEdge(true);
109575           }
109576         };
109577         LineBuilder.prototype.interfaces_ = function interfaces_ () {
109578           return []
109579         };
109580         LineBuilder.prototype.getClass = function getClass () {
109581           return LineBuilder
109582         };
109583
109584         var PointBuilder = function PointBuilder () {
109585           this._op = null;
109586           this._geometryFactory = null;
109587           this._resultPointList = new ArrayList();
109588           var op = arguments[0];
109589           var geometryFactory = arguments[1];
109590           // const ptLocator = arguments[2]
109591           this._op = op;
109592           this._geometryFactory = geometryFactory;
109593         };
109594         PointBuilder.prototype.filterCoveredNodeToPoint = function filterCoveredNodeToPoint (n) {
109595           var coord = n.getCoordinate();
109596           if (!this._op.isCoveredByLA(coord)) {
109597             var pt = this._geometryFactory.createPoint(coord);
109598             this._resultPointList.add(pt);
109599           }
109600         };
109601         PointBuilder.prototype.extractNonCoveredResultNodes = function extractNonCoveredResultNodes (opCode) {
109602             var this$1 = this;
109603
109604           for (var nodeit = this._op.getGraph().getNodes().iterator(); nodeit.hasNext();) {
109605             var n = nodeit.next();
109606             if (n.isInResult()) { continue }
109607             if (n.isIncidentEdgeInResult()) { continue }
109608             if (n.getEdges().getDegree() === 0 || opCode === OverlayOp.INTERSECTION) {
109609               var label = n.getLabel();
109610               if (OverlayOp.isResultOfOp(label, opCode)) {
109611                 this$1.filterCoveredNodeToPoint(n);
109612               }
109613             }
109614           }
109615         };
109616         PointBuilder.prototype.build = function build (opCode) {
109617           this.extractNonCoveredResultNodes(opCode);
109618           return this._resultPointList
109619         };
109620         PointBuilder.prototype.interfaces_ = function interfaces_ () {
109621           return []
109622         };
109623         PointBuilder.prototype.getClass = function getClass () {
109624           return PointBuilder
109625         };
109626
109627         var GeometryTransformer = function GeometryTransformer () {
109628           this._inputGeom = null;
109629           this._factory = null;
109630           this._pruneEmptyGeometry = true;
109631           this._preserveGeometryCollectionType = true;
109632           this._preserveCollections = false;
109633           this._preserveType = false;
109634         };
109635         GeometryTransformer.prototype.transformPoint = function transformPoint (geom, parent) {
109636           return this._factory.createPoint(this.transformCoordinates(geom.getCoordinateSequence(), geom))
109637         };
109638         GeometryTransformer.prototype.transformPolygon = function transformPolygon (geom, parent) {
109639             var this$1 = this;
109640
109641           var isAllValidLinearRings = true;
109642           var shell = this.transformLinearRing(geom.getExteriorRing(), geom);
109643           if (shell === null || !(shell instanceof LinearRing) || shell.isEmpty()) { isAllValidLinearRings = false; }
109644           var holes = new ArrayList();
109645           for (var i = 0; i < geom.getNumInteriorRing(); i++) {
109646             var hole = this$1.transformLinearRing(geom.getInteriorRingN(i), geom);
109647             if (hole === null || hole.isEmpty()) {
109648               continue
109649             }
109650             if (!(hole instanceof LinearRing)) { isAllValidLinearRings = false; }
109651             holes.add(hole);
109652           }
109653           if (isAllValidLinearRings) { return this._factory.createPolygon(shell, holes.toArray([])); } else {
109654             var components = new ArrayList();
109655             if (shell !== null) { components.add(shell); }
109656             components.addAll(holes);
109657             return this._factory.buildGeometry(components)
109658           }
109659         };
109660         GeometryTransformer.prototype.createCoordinateSequence = function createCoordinateSequence (coords) {
109661           return this._factory.getCoordinateSequenceFactory().create(coords)
109662         };
109663         GeometryTransformer.prototype.getInputGeometry = function getInputGeometry () {
109664           return this._inputGeom
109665         };
109666         GeometryTransformer.prototype.transformMultiLineString = function transformMultiLineString (geom, parent) {
109667             var this$1 = this;
109668
109669           var transGeomList = new ArrayList();
109670           for (var i = 0; i < geom.getNumGeometries(); i++) {
109671             var transformGeom = this$1.transformLineString(geom.getGeometryN(i), geom);
109672             if (transformGeom === null) { continue }
109673             if (transformGeom.isEmpty()) { continue }
109674             transGeomList.add(transformGeom);
109675           }
109676           return this._factory.buildGeometry(transGeomList)
109677         };
109678         GeometryTransformer.prototype.transformCoordinates = function transformCoordinates (coords, parent) {
109679           return this.copy(coords)
109680         };
109681         GeometryTransformer.prototype.transformLineString = function transformLineString (geom, parent) {
109682           return this._factory.createLineString(this.transformCoordinates(geom.getCoordinateSequence(), geom))
109683         };
109684         GeometryTransformer.prototype.transformMultiPoint = function transformMultiPoint (geom, parent) {
109685             var this$1 = this;
109686
109687           var transGeomList = new ArrayList();
109688           for (var i = 0; i < geom.getNumGeometries(); i++) {
109689             var transformGeom = this$1.transformPoint(geom.getGeometryN(i), geom);
109690             if (transformGeom === null) { continue }
109691             if (transformGeom.isEmpty()) { continue }
109692             transGeomList.add(transformGeom);
109693           }
109694           return this._factory.buildGeometry(transGeomList)
109695         };
109696         GeometryTransformer.prototype.transformMultiPolygon = function transformMultiPolygon (geom, parent) {
109697             var this$1 = this;
109698
109699           var transGeomList = new ArrayList();
109700           for (var i = 0; i < geom.getNumGeometries(); i++) {
109701             var transformGeom = this$1.transformPolygon(geom.getGeometryN(i), geom);
109702             if (transformGeom === null) { continue }
109703             if (transformGeom.isEmpty()) { continue }
109704             transGeomList.add(transformGeom);
109705           }
109706           return this._factory.buildGeometry(transGeomList)
109707         };
109708         GeometryTransformer.prototype.copy = function copy (seq) {
109709           return seq.copy()
109710         };
109711         GeometryTransformer.prototype.transformGeometryCollection = function transformGeometryCollection (geom, parent) {
109712             var this$1 = this;
109713
109714           var transGeomList = new ArrayList();
109715           for (var i = 0; i < geom.getNumGeometries(); i++) {
109716             var transformGeom = this$1.transform(geom.getGeometryN(i));
109717             if (transformGeom === null) { continue }
109718             if (this$1._pruneEmptyGeometry && transformGeom.isEmpty()) { continue }
109719             transGeomList.add(transformGeom);
109720           }
109721           if (this._preserveGeometryCollectionType) { return this._factory.createGeometryCollection(GeometryFactory.toGeometryArray(transGeomList)) }
109722           return this._factory.buildGeometry(transGeomList)
109723         };
109724         GeometryTransformer.prototype.transform = function transform (inputGeom) {
109725           this._inputGeom = inputGeom;
109726           this._factory = inputGeom.getFactory();
109727           if (inputGeom instanceof Point$1) { return this.transformPoint(inputGeom, null) }
109728           if (inputGeom instanceof MultiPoint) { return this.transformMultiPoint(inputGeom, null) }
109729           if (inputGeom instanceof LinearRing) { return this.transformLinearRing(inputGeom, null) }
109730           if (inputGeom instanceof LineString) { return this.transformLineString(inputGeom, null) }
109731           if (inputGeom instanceof MultiLineString) { return this.transformMultiLineString(inputGeom, null) }
109732           if (inputGeom instanceof Polygon) { return this.transformPolygon(inputGeom, null) }
109733           if (inputGeom instanceof MultiPolygon) { return this.transformMultiPolygon(inputGeom, null) }
109734           if (inputGeom instanceof GeometryCollection) { return this.transformGeometryCollection(inputGeom, null) }
109735           throw new IllegalArgumentException('Unknown Geometry subtype: ' + inputGeom.getClass().getName())
109736         };
109737         GeometryTransformer.prototype.transformLinearRing = function transformLinearRing (geom, parent) {
109738           var seq = this.transformCoordinates(geom.getCoordinateSequence(), geom);
109739           if (seq === null) { return this._factory.createLinearRing(null) }
109740           var seqSize = seq.size();
109741           if (seqSize > 0 && seqSize < 4 && !this._preserveType) { return this._factory.createLineString(seq) }
109742           return this._factory.createLinearRing(seq)
109743         };
109744         GeometryTransformer.prototype.interfaces_ = function interfaces_ () {
109745           return []
109746         };
109747         GeometryTransformer.prototype.getClass = function getClass () {
109748           return GeometryTransformer
109749         };
109750
109751         var LineStringSnapper = function LineStringSnapper () {
109752           this._snapTolerance = 0.0;
109753           this._srcPts = null;
109754           this._seg = new LineSegment();
109755           this._allowSnappingToSourceVertices = false;
109756           this._isClosed = false;
109757           if (arguments[0] instanceof LineString && typeof arguments[1] === 'number') {
109758             var srcLine = arguments[0];
109759             var snapTolerance = arguments[1];
109760             LineStringSnapper.call(this, srcLine.getCoordinates(), snapTolerance);
109761           } else if (arguments[0] instanceof Array && typeof arguments[1] === 'number') {
109762             var srcPts = arguments[0];
109763             var snapTolerance$1 = arguments[1];
109764             this._srcPts = srcPts;
109765             this._isClosed = LineStringSnapper.isClosed(srcPts);
109766             this._snapTolerance = snapTolerance$1;
109767           }
109768         };
109769         LineStringSnapper.prototype.snapVertices = function snapVertices (srcCoords, snapPts) {
109770             var this$1 = this;
109771
109772           var end = this._isClosed ? srcCoords.size() - 1 : srcCoords.size();
109773           for (var i = 0; i < end; i++) {
109774             var srcPt = srcCoords.get(i);
109775             var snapVert = this$1.findSnapForVertex(srcPt, snapPts);
109776             if (snapVert !== null) {
109777               srcCoords.set(i, new Coordinate(snapVert));
109778               if (i === 0 && this$1._isClosed) { srcCoords.set(srcCoords.size() - 1, new Coordinate(snapVert)); }
109779             }
109780           }
109781         };
109782         LineStringSnapper.prototype.findSnapForVertex = function findSnapForVertex (pt, snapPts) {
109783             var this$1 = this;
109784
109785           for (var i = 0; i < snapPts.length; i++) {
109786             if (pt.equals2D(snapPts[i])) { return null }
109787             if (pt.distance(snapPts[i]) < this$1._snapTolerance) { return snapPts[i] }
109788           }
109789           return null
109790         };
109791         LineStringSnapper.prototype.snapTo = function snapTo (snapPts) {
109792           var coordList = new CoordinateList(this._srcPts);
109793           this.snapVertices(coordList, snapPts);
109794           this.snapSegments(coordList, snapPts);
109795           var newPts = coordList.toCoordinateArray();
109796           return newPts
109797         };
109798         LineStringSnapper.prototype.snapSegments = function snapSegments (srcCoords, snapPts) {
109799             var this$1 = this;
109800
109801           if (snapPts.length === 0) { return null }
109802           var distinctPtCount = snapPts.length;
109803           if (snapPts[0].equals2D(snapPts[snapPts.length - 1])) { distinctPtCount = snapPts.length - 1; }
109804           for (var i = 0; i < distinctPtCount; i++) {
109805             var snapPt = snapPts[i];
109806             var index = this$1.findSegmentIndexToSnap(snapPt, srcCoords);
109807             if (index >= 0) {
109808               srcCoords.add(index + 1, new Coordinate(snapPt), false);
109809             }
109810           }
109811         };
109812         LineStringSnapper.prototype.findSegmentIndexToSnap = function findSegmentIndexToSnap (snapPt, srcCoords) {
109813             var this$1 = this;
109814
109815           var minDist = Double.MAX_VALUE;
109816           var snapIndex = -1;
109817           for (var i = 0; i < srcCoords.size() - 1; i++) {
109818             this$1._seg.p0 = srcCoords.get(i);
109819             this$1._seg.p1 = srcCoords.get(i + 1);
109820             if (this$1._seg.p0.equals2D(snapPt) || this$1._seg.p1.equals2D(snapPt)) {
109821               if (this$1._allowSnappingToSourceVertices) { continue; } else { return -1 }
109822             }
109823             var dist = this$1._seg.distance(snapPt);
109824             if (dist < this$1._snapTolerance && dist < minDist) {
109825               minDist = dist;
109826               snapIndex = i;
109827             }
109828           }
109829           return snapIndex
109830         };
109831         LineStringSnapper.prototype.setAllowSnappingToSourceVertices = function setAllowSnappingToSourceVertices (allowSnappingToSourceVertices) {
109832           this._allowSnappingToSourceVertices = allowSnappingToSourceVertices;
109833         };
109834         LineStringSnapper.prototype.interfaces_ = function interfaces_ () {
109835           return []
109836         };
109837         LineStringSnapper.prototype.getClass = function getClass () {
109838           return LineStringSnapper
109839         };
109840         LineStringSnapper.isClosed = function isClosed (pts) {
109841           if (pts.length <= 1) { return false }
109842           return pts[0].equals2D(pts[pts.length - 1])
109843         };
109844
109845         var GeometrySnapper = function GeometrySnapper (srcGeom) {
109846           this._srcGeom = srcGeom || null;
109847         };
109848
109849         var staticAccessors$41 = { SNAP_PRECISION_FACTOR: { configurable: true } };
109850         GeometrySnapper.prototype.snapTo = function snapTo (snapGeom, snapTolerance) {
109851           var snapPts = this.extractTargetCoordinates(snapGeom);
109852           var snapTrans = new SnapTransformer(snapTolerance, snapPts);
109853           return snapTrans.transform(this._srcGeom)
109854         };
109855         GeometrySnapper.prototype.snapToSelf = function snapToSelf (snapTolerance, cleanResult) {
109856           var snapPts = this.extractTargetCoordinates(this._srcGeom);
109857           var snapTrans = new SnapTransformer(snapTolerance, snapPts, true);
109858           var snappedGeom = snapTrans.transform(this._srcGeom);
109859           var result = snappedGeom;
109860           if (cleanResult && hasInterface(result, Polygonal)) {
109861             result = snappedGeom.buffer(0);
109862           }
109863           return result
109864         };
109865         GeometrySnapper.prototype.computeSnapTolerance = function computeSnapTolerance (ringPts) {
109866           var minSegLen = this.computeMinimumSegmentLength(ringPts);
109867           var snapTol = minSegLen / 10;
109868           return snapTol
109869         };
109870         GeometrySnapper.prototype.extractTargetCoordinates = function extractTargetCoordinates (g) {
109871           var ptSet = new TreeSet();
109872           var pts = g.getCoordinates();
109873           for (var i = 0; i < pts.length; i++) {
109874             ptSet.add(pts[i]);
109875           }
109876           return ptSet.toArray(new Array(0).fill(null))
109877         };
109878         GeometrySnapper.prototype.computeMinimumSegmentLength = function computeMinimumSegmentLength (pts) {
109879           var minSegLen = Double.MAX_VALUE;
109880           for (var i = 0; i < pts.length - 1; i++) {
109881             var segLen = pts[i].distance(pts[i + 1]);
109882             if (segLen < minSegLen) { minSegLen = segLen; }
109883           }
109884           return minSegLen
109885         };
109886         GeometrySnapper.prototype.interfaces_ = function interfaces_ () {
109887           return []
109888         };
109889         GeometrySnapper.prototype.getClass = function getClass () {
109890           return GeometrySnapper
109891         };
109892         GeometrySnapper.snap = function snap (g0, g1, snapTolerance) {
109893           var snapGeom = new Array(2).fill(null);
109894           var snapper0 = new GeometrySnapper(g0);
109895           snapGeom[0] = snapper0.snapTo(g1, snapTolerance);
109896           var snapper1 = new GeometrySnapper(g1);
109897           snapGeom[1] = snapper1.snapTo(snapGeom[0], snapTolerance);
109898           return snapGeom
109899         };
109900         GeometrySnapper.computeOverlaySnapTolerance = function computeOverlaySnapTolerance () {
109901           if (arguments.length === 1) {
109902             var g = arguments[0];
109903             var snapTolerance = GeometrySnapper.computeSizeBasedSnapTolerance(g);
109904             var pm = g.getPrecisionModel();
109905             if (pm.getType() === PrecisionModel.FIXED) {
109906               var fixedSnapTol = 1 / pm.getScale() * 2 / 1.415;
109907               if (fixedSnapTol > snapTolerance) { snapTolerance = fixedSnapTol; }
109908             }
109909             return snapTolerance
109910           } else if (arguments.length === 2) {
109911             var g0 = arguments[0];
109912             var g1 = arguments[1];
109913             return Math.min(GeometrySnapper.computeOverlaySnapTolerance(g0), GeometrySnapper.computeOverlaySnapTolerance(g1))
109914           }
109915         };
109916         GeometrySnapper.computeSizeBasedSnapTolerance = function computeSizeBasedSnapTolerance (g) {
109917           var env = g.getEnvelopeInternal();
109918           var minDimension = Math.min(env.getHeight(), env.getWidth());
109919           var snapTol = minDimension * GeometrySnapper.SNAP_PRECISION_FACTOR;
109920           return snapTol
109921         };
109922         GeometrySnapper.snapToSelf = function snapToSelf (geom, snapTolerance, cleanResult) {
109923           var snapper0 = new GeometrySnapper(geom);
109924           return snapper0.snapToSelf(snapTolerance, cleanResult)
109925         };
109926         staticAccessors$41.SNAP_PRECISION_FACTOR.get = function () { return 1e-9 };
109927
109928         Object.defineProperties( GeometrySnapper, staticAccessors$41 );
109929
109930         var SnapTransformer = (function (GeometryTransformer$$1) {
109931           function SnapTransformer (snapTolerance, snapPts, isSelfSnap) {
109932             GeometryTransformer$$1.call(this);
109933             this._snapTolerance = snapTolerance || null;
109934             this._snapPts = snapPts || null;
109935             this._isSelfSnap = (isSelfSnap !== undefined) ? isSelfSnap : false;
109936           }
109937
109938           if ( GeometryTransformer$$1 ) { SnapTransformer.__proto__ = GeometryTransformer$$1; }
109939           SnapTransformer.prototype = Object.create( GeometryTransformer$$1 && GeometryTransformer$$1.prototype );
109940           SnapTransformer.prototype.constructor = SnapTransformer;
109941           SnapTransformer.prototype.snapLine = function snapLine (srcPts, snapPts) {
109942             var snapper = new LineStringSnapper(srcPts, this._snapTolerance);
109943             snapper.setAllowSnappingToSourceVertices(this._isSelfSnap);
109944             return snapper.snapTo(snapPts)
109945           };
109946           SnapTransformer.prototype.transformCoordinates = function transformCoordinates (coords, parent) {
109947             var srcPts = coords.toCoordinateArray();
109948             var newPts = this.snapLine(srcPts, this._snapPts);
109949             return this._factory.getCoordinateSequenceFactory().create(newPts)
109950           };
109951           SnapTransformer.prototype.interfaces_ = function interfaces_ () {
109952             return []
109953           };
109954           SnapTransformer.prototype.getClass = function getClass () {
109955             return SnapTransformer
109956           };
109957
109958           return SnapTransformer;
109959         }(GeometryTransformer));
109960
109961         var CommonBits = function CommonBits () {
109962           this._isFirst = true;
109963           this._commonMantissaBitsCount = 53;
109964           this._commonBits = 0;
109965           this._commonSignExp = null;
109966         };
109967         CommonBits.prototype.getCommon = function getCommon () {
109968           return Double.longBitsToDouble(this._commonBits)
109969         };
109970         CommonBits.prototype.add = function add (num) {
109971           var numBits = Double.doubleToLongBits(num);
109972           if (this._isFirst) {
109973             this._commonBits = numBits;
109974             this._commonSignExp = CommonBits.signExpBits(this._commonBits);
109975             this._isFirst = false;
109976             return null
109977           }
109978           var numSignExp = CommonBits.signExpBits(numBits);
109979           if (numSignExp !== this._commonSignExp) {
109980             this._commonBits = 0;
109981             return null
109982           }
109983           this._commonMantissaBitsCount = CommonBits.numCommonMostSigMantissaBits(this._commonBits, numBits);
109984           this._commonBits = CommonBits.zeroLowerBits(this._commonBits, 64 - (12 + this._commonMantissaBitsCount));
109985         };
109986         CommonBits.prototype.toString = function toString () {
109987           if (arguments.length === 1) {
109988             var bits = arguments[0];
109989             var x = Double.longBitsToDouble(bits);
109990             var numStr = Double.toBinaryString(bits);
109991             var padStr = '0000000000000000000000000000000000000000000000000000000000000000' + numStr;
109992             var bitStr = padStr.substring(padStr.length - 64);
109993             var str = bitStr.substring(0, 1) + '  ' + bitStr.substring(1, 12) + '(exp) ' + bitStr.substring(12) + ' [ ' + x + ' ]';
109994             return str
109995           }
109996         };
109997         CommonBits.prototype.interfaces_ = function interfaces_ () {
109998           return []
109999         };
110000         CommonBits.prototype.getClass = function getClass () {
110001           return CommonBits
110002         };
110003         CommonBits.getBit = function getBit (bits, i) {
110004           var mask = 1 << i;
110005           return (bits & mask) !== 0 ? 1 : 0
110006         };
110007         CommonBits.signExpBits = function signExpBits (num) {
110008           return num >> 52
110009         };
110010         CommonBits.zeroLowerBits = function zeroLowerBits (bits, nBits) {
110011           var invMask = (1 << nBits) - 1;
110012           var mask = ~invMask;
110013           var zeroed = bits & mask;
110014           return zeroed
110015         };
110016         CommonBits.numCommonMostSigMantissaBits = function numCommonMostSigMantissaBits (num1, num2) {
110017           var count = 0;
110018           for (var i = 52; i >= 0; i--) {
110019             if (CommonBits.getBit(num1, i) !== CommonBits.getBit(num2, i)) { return count }
110020             count++;
110021           }
110022           return 52
110023         };
110024
110025         var CommonBitsRemover = function CommonBitsRemover () {
110026           this._commonCoord = null;
110027           this._ccFilter = new CommonCoordinateFilter();
110028         };
110029
110030         var staticAccessors$42 = { CommonCoordinateFilter: { configurable: true },Translater: { configurable: true } };
110031         CommonBitsRemover.prototype.addCommonBits = function addCommonBits (geom) {
110032           var trans = new Translater(this._commonCoord);
110033           geom.apply(trans);
110034           geom.geometryChanged();
110035         };
110036         CommonBitsRemover.prototype.removeCommonBits = function removeCommonBits (geom) {
110037           if (this._commonCoord.x === 0.0 && this._commonCoord.y === 0.0) { return geom }
110038           var invCoord = new Coordinate(this._commonCoord);
110039           invCoord.x = -invCoord.x;
110040           invCoord.y = -invCoord.y;
110041           var trans = new Translater(invCoord);
110042           geom.apply(trans);
110043           geom.geometryChanged();
110044           return geom
110045         };
110046         CommonBitsRemover.prototype.getCommonCoordinate = function getCommonCoordinate () {
110047           return this._commonCoord
110048         };
110049         CommonBitsRemover.prototype.add = function add (geom) {
110050           geom.apply(this._ccFilter);
110051           this._commonCoord = this._ccFilter.getCommonCoordinate();
110052         };
110053         CommonBitsRemover.prototype.interfaces_ = function interfaces_ () {
110054           return []
110055         };
110056         CommonBitsRemover.prototype.getClass = function getClass () {
110057           return CommonBitsRemover
110058         };
110059         staticAccessors$42.CommonCoordinateFilter.get = function () { return CommonCoordinateFilter };
110060         staticAccessors$42.Translater.get = function () { return Translater };
110061
110062         Object.defineProperties( CommonBitsRemover, staticAccessors$42 );
110063
110064         var CommonCoordinateFilter = function CommonCoordinateFilter () {
110065           this._commonBitsX = new CommonBits();
110066           this._commonBitsY = new CommonBits();
110067         };
110068         CommonCoordinateFilter.prototype.filter = function filter (coord) {
110069           this._commonBitsX.add(coord.x);
110070           this._commonBitsY.add(coord.y);
110071         };
110072         CommonCoordinateFilter.prototype.getCommonCoordinate = function getCommonCoordinate () {
110073           return new Coordinate(this._commonBitsX.getCommon(), this._commonBitsY.getCommon())
110074         };
110075         CommonCoordinateFilter.prototype.interfaces_ = function interfaces_ () {
110076           return [CoordinateFilter]
110077         };
110078         CommonCoordinateFilter.prototype.getClass = function getClass () {
110079           return CommonCoordinateFilter
110080         };
110081
110082         var Translater = function Translater () {
110083           this.trans = null;
110084           var trans = arguments[0];
110085           this.trans = trans;
110086         };
110087         Translater.prototype.filter = function filter (seq, i) {
110088           var xp = seq.getOrdinate(i, 0) + this.trans.x;
110089           var yp = seq.getOrdinate(i, 1) + this.trans.y;
110090           seq.setOrdinate(i, 0, xp);
110091           seq.setOrdinate(i, 1, yp);
110092         };
110093         Translater.prototype.isDone = function isDone () {
110094           return false
110095         };
110096         Translater.prototype.isGeometryChanged = function isGeometryChanged () {
110097           return true
110098         };
110099         Translater.prototype.interfaces_ = function interfaces_ () {
110100           return [CoordinateSequenceFilter]
110101         };
110102         Translater.prototype.getClass = function getClass () {
110103           return Translater
110104         };
110105
110106         var SnapOverlayOp = function SnapOverlayOp (g1, g2) {
110107           this._geom = new Array(2).fill(null);
110108           this._snapTolerance = null;
110109           this._cbr = null;
110110           this._geom[0] = g1;
110111           this._geom[1] = g2;
110112           this.computeSnapTolerance();
110113         };
110114         SnapOverlayOp.prototype.selfSnap = function selfSnap (geom) {
110115           var snapper0 = new GeometrySnapper(geom);
110116           var snapGeom = snapper0.snapTo(geom, this._snapTolerance);
110117           return snapGeom
110118         };
110119         SnapOverlayOp.prototype.removeCommonBits = function removeCommonBits (geom) {
110120           this._cbr = new CommonBitsRemover();
110121           this._cbr.add(geom[0]);
110122           this._cbr.add(geom[1]);
110123           var remGeom = new Array(2).fill(null);
110124           remGeom[0] = this._cbr.removeCommonBits(geom[0].copy());
110125           remGeom[1] = this._cbr.removeCommonBits(geom[1].copy());
110126           return remGeom
110127         };
110128         SnapOverlayOp.prototype.prepareResult = function prepareResult (geom) {
110129           this._cbr.addCommonBits(geom);
110130           return geom
110131         };
110132         SnapOverlayOp.prototype.getResultGeometry = function getResultGeometry (opCode) {
110133           var prepGeom = this.snap(this._geom);
110134           var result = OverlayOp.overlayOp(prepGeom[0], prepGeom[1], opCode);
110135           return this.prepareResult(result)
110136         };
110137         SnapOverlayOp.prototype.checkValid = function checkValid (g) {
110138           if (!g.isValid()) {
110139             System.out.println('Snapped geometry is invalid');
110140           }
110141         };
110142         SnapOverlayOp.prototype.computeSnapTolerance = function computeSnapTolerance () {
110143           this._snapTolerance = GeometrySnapper.computeOverlaySnapTolerance(this._geom[0], this._geom[1]);
110144         };
110145         SnapOverlayOp.prototype.snap = function snap (geom) {
110146           var remGeom = this.removeCommonBits(geom);
110147           var snapGeom = GeometrySnapper.snap(remGeom[0], remGeom[1], this._snapTolerance);
110148           return snapGeom
110149         };
110150         SnapOverlayOp.prototype.interfaces_ = function interfaces_ () {
110151           return []
110152         };
110153         SnapOverlayOp.prototype.getClass = function getClass () {
110154           return SnapOverlayOp
110155         };
110156         SnapOverlayOp.overlayOp = function overlayOp (g0, g1, opCode) {
110157           var op = new SnapOverlayOp(g0, g1);
110158           return op.getResultGeometry(opCode)
110159         };
110160         SnapOverlayOp.union = function union (g0, g1) {
110161           return SnapOverlayOp.overlayOp(g0, g1, OverlayOp.UNION)
110162         };
110163         SnapOverlayOp.intersection = function intersection (g0, g1) {
110164           return SnapOverlayOp.overlayOp(g0, g1, OverlayOp.INTERSECTION)
110165         };
110166         SnapOverlayOp.symDifference = function symDifference (g0, g1) {
110167           return SnapOverlayOp.overlayOp(g0, g1, OverlayOp.SYMDIFFERENCE)
110168         };
110169         SnapOverlayOp.difference = function difference (g0, g1) {
110170           return SnapOverlayOp.overlayOp(g0, g1, OverlayOp.DIFFERENCE)
110171         };
110172
110173         var SnapIfNeededOverlayOp = function SnapIfNeededOverlayOp (g1, g2) {
110174           this._geom = new Array(2).fill(null);
110175           this._geom[0] = g1;
110176           this._geom[1] = g2;
110177         };
110178         SnapIfNeededOverlayOp.prototype.getResultGeometry = function getResultGeometry (opCode) {
110179           var result = null;
110180           var isSuccess = false;
110181           var savedException = null;
110182           try {
110183             result = OverlayOp.overlayOp(this._geom[0], this._geom[1], opCode);
110184             var isValid = true;
110185             if (isValid) { isSuccess = true; }
110186           } catch (ex) {
110187             if (ex instanceof RuntimeException) {
110188               savedException = ex;
110189             } else { throw ex }
110190           } finally {}
110191           if (!isSuccess) {
110192             try {
110193               result = SnapOverlayOp.overlayOp(this._geom[0], this._geom[1], opCode);
110194             } catch (ex$1) {
110195               if (ex$1 instanceof RuntimeException) {
110196                 throw savedException
110197               } else { throw ex$1 }
110198             } finally {}
110199           }
110200           return result
110201         };
110202         SnapIfNeededOverlayOp.prototype.interfaces_ = function interfaces_ () {
110203           return []
110204         };
110205         SnapIfNeededOverlayOp.prototype.getClass = function getClass () {
110206           return SnapIfNeededOverlayOp
110207         };
110208         SnapIfNeededOverlayOp.overlayOp = function overlayOp (g0, g1, opCode) {
110209           var op = new SnapIfNeededOverlayOp(g0, g1);
110210           return op.getResultGeometry(opCode)
110211         };
110212         SnapIfNeededOverlayOp.union = function union (g0, g1) {
110213           return SnapIfNeededOverlayOp.overlayOp(g0, g1, OverlayOp.UNION)
110214         };
110215         SnapIfNeededOverlayOp.intersection = function intersection (g0, g1) {
110216           return SnapIfNeededOverlayOp.overlayOp(g0, g1, OverlayOp.INTERSECTION)
110217         };
110218         SnapIfNeededOverlayOp.symDifference = function symDifference (g0, g1) {
110219           return SnapIfNeededOverlayOp.overlayOp(g0, g1, OverlayOp.SYMDIFFERENCE)
110220         };
110221         SnapIfNeededOverlayOp.difference = function difference (g0, g1) {
110222           return SnapIfNeededOverlayOp.overlayOp(g0, g1, OverlayOp.DIFFERENCE)
110223         };
110224
110225         var MonotoneChain$2 = function MonotoneChain () {
110226           this.mce = null;
110227           this.chainIndex = null;
110228           var mce = arguments[0];
110229           var chainIndex = arguments[1];
110230           this.mce = mce;
110231           this.chainIndex = chainIndex;
110232         };
110233         MonotoneChain$2.prototype.computeIntersections = function computeIntersections (mc, si) {
110234           this.mce.computeIntersectsForChain(this.chainIndex, mc.mce, mc.chainIndex, si);
110235         };
110236         MonotoneChain$2.prototype.interfaces_ = function interfaces_ () {
110237           return []
110238         };
110239         MonotoneChain$2.prototype.getClass = function getClass () {
110240           return MonotoneChain$2
110241         };
110242
110243         var SweepLineEvent = function SweepLineEvent () {
110244           this._label = null;
110245           this._xValue = null;
110246           this._eventType = null;
110247           this._insertEvent = null;
110248           this._deleteEventIndex = null;
110249           this._obj = null;
110250           if (arguments.length === 2) {
110251             var x = arguments[0];
110252             var insertEvent = arguments[1];
110253             this._eventType = SweepLineEvent.DELETE;
110254             this._xValue = x;
110255             this._insertEvent = insertEvent;
110256           } else if (arguments.length === 3) {
110257             var label = arguments[0];
110258             var x$1 = arguments[1];
110259             var obj = arguments[2];
110260             this._eventType = SweepLineEvent.INSERT;
110261             this._label = label;
110262             this._xValue = x$1;
110263             this._obj = obj;
110264           }
110265         };
110266
110267         var staticAccessors$43 = { INSERT: { configurable: true },DELETE: { configurable: true } };
110268         SweepLineEvent.prototype.isDelete = function isDelete () {
110269           return this._eventType === SweepLineEvent.DELETE
110270         };
110271         SweepLineEvent.prototype.setDeleteEventIndex = function setDeleteEventIndex (deleteEventIndex) {
110272           this._deleteEventIndex = deleteEventIndex;
110273         };
110274         SweepLineEvent.prototype.getObject = function getObject () {
110275           return this._obj
110276         };
110277         SweepLineEvent.prototype.compareTo = function compareTo (o) {
110278           var pe = o;
110279           if (this._xValue < pe._xValue) { return -1 }
110280           if (this._xValue > pe._xValue) { return 1 }
110281           if (this._eventType < pe._eventType) { return -1 }
110282           if (this._eventType > pe._eventType) { return 1 }
110283           return 0
110284         };
110285         SweepLineEvent.prototype.getInsertEvent = function getInsertEvent () {
110286           return this._insertEvent
110287         };
110288         SweepLineEvent.prototype.isInsert = function isInsert () {
110289           return this._eventType === SweepLineEvent.INSERT
110290         };
110291         SweepLineEvent.prototype.isSameLabel = function isSameLabel (ev) {
110292           if (this._label === null) { return false }
110293           return this._label === ev._label
110294         };
110295         SweepLineEvent.prototype.getDeleteEventIndex = function getDeleteEventIndex () {
110296           return this._deleteEventIndex
110297         };
110298         SweepLineEvent.prototype.interfaces_ = function interfaces_ () {
110299           return [Comparable]
110300         };
110301         SweepLineEvent.prototype.getClass = function getClass () {
110302           return SweepLineEvent
110303         };
110304         staticAccessors$43.INSERT.get = function () { return 1 };
110305         staticAccessors$43.DELETE.get = function () { return 2 };
110306
110307         Object.defineProperties( SweepLineEvent, staticAccessors$43 );
110308
110309         var EdgeSetIntersector = function EdgeSetIntersector () {};
110310
110311         EdgeSetIntersector.prototype.interfaces_ = function interfaces_ () {
110312           return []
110313         };
110314         EdgeSetIntersector.prototype.getClass = function getClass () {
110315           return EdgeSetIntersector
110316         };
110317
110318         var SegmentIntersector$2 = function SegmentIntersector () {
110319           this._hasIntersection = false;
110320           this._hasProper = false;
110321           this._hasProperInterior = false;
110322           this._properIntersectionPoint = null;
110323           this._li = null;
110324           this._includeProper = null;
110325           this._recordIsolated = null;
110326           this._isSelfIntersection = null;
110327           this._numIntersections = 0;
110328           this.numTests = 0;
110329           this._bdyNodes = null;
110330           this._isDone = false;
110331           this._isDoneWhenProperInt = false;
110332           var li = arguments[0];
110333           var includeProper = arguments[1];
110334           var recordIsolated = arguments[2];
110335           this._li = li;
110336           this._includeProper = includeProper;
110337           this._recordIsolated = recordIsolated;
110338         };
110339         SegmentIntersector$2.prototype.isTrivialIntersection = function isTrivialIntersection (e0, segIndex0, e1, segIndex1) {
110340           if (e0 === e1) {
110341             if (this._li.getIntersectionNum() === 1) {
110342               if (SegmentIntersector$2.isAdjacentSegments(segIndex0, segIndex1)) { return true }
110343               if (e0.isClosed()) {
110344                 var maxSegIndex = e0.getNumPoints() - 1;
110345                 if ((segIndex0 === 0 && segIndex1 === maxSegIndex) ||
110346                     (segIndex1 === 0 && segIndex0 === maxSegIndex)) {
110347                   return true
110348                 }
110349               }
110350             }
110351           }
110352           return false
110353         };
110354         SegmentIntersector$2.prototype.getProperIntersectionPoint = function getProperIntersectionPoint () {
110355           return this._properIntersectionPoint
110356         };
110357         SegmentIntersector$2.prototype.setIsDoneIfProperInt = function setIsDoneIfProperInt (isDoneWhenProperInt) {
110358           this._isDoneWhenProperInt = isDoneWhenProperInt;
110359         };
110360         SegmentIntersector$2.prototype.hasProperInteriorIntersection = function hasProperInteriorIntersection () {
110361           return this._hasProperInterior
110362         };
110363         SegmentIntersector$2.prototype.isBoundaryPointInternal = function isBoundaryPointInternal (li, bdyNodes) {
110364           for (var i = bdyNodes.iterator(); i.hasNext();) {
110365             var node = i.next();
110366             var pt = node.getCoordinate();
110367             if (li.isIntersection(pt)) { return true }
110368           }
110369           return false
110370         };
110371         SegmentIntersector$2.prototype.hasProperIntersection = function hasProperIntersection () {
110372           return this._hasProper
110373         };
110374         SegmentIntersector$2.prototype.hasIntersection = function hasIntersection () {
110375           return this._hasIntersection
110376         };
110377         SegmentIntersector$2.prototype.isDone = function isDone () {
110378           return this._isDone
110379         };
110380         SegmentIntersector$2.prototype.isBoundaryPoint = function isBoundaryPoint (li, bdyNodes) {
110381           if (bdyNodes === null) { return false }
110382           if (this.isBoundaryPointInternal(li, bdyNodes[0])) { return true }
110383           if (this.isBoundaryPointInternal(li, bdyNodes[1])) { return true }
110384           return false
110385         };
110386         SegmentIntersector$2.prototype.setBoundaryNodes = function setBoundaryNodes (bdyNodes0, bdyNodes1) {
110387           this._bdyNodes = new Array(2).fill(null);
110388           this._bdyNodes[0] = bdyNodes0;
110389           this._bdyNodes[1] = bdyNodes1;
110390         };
110391         SegmentIntersector$2.prototype.addIntersections = function addIntersections (e0, segIndex0, e1, segIndex1) {
110392           if (e0 === e1 && segIndex0 === segIndex1) { return null }
110393           this.numTests++;
110394           var p00 = e0.getCoordinates()[segIndex0];
110395           var p01 = e0.getCoordinates()[segIndex0 + 1];
110396           var p10 = e1.getCoordinates()[segIndex1];
110397           var p11 = e1.getCoordinates()[segIndex1 + 1];
110398           this._li.computeIntersection(p00, p01, p10, p11);
110399           if (this._li.hasIntersection()) {
110400             if (this._recordIsolated) {
110401               e0.setIsolated(false);
110402               e1.setIsolated(false);
110403             }
110404             this._numIntersections++;
110405             if (!this.isTrivialIntersection(e0, segIndex0, e1, segIndex1)) {
110406               this._hasIntersection = true;
110407               if (this._includeProper || !this._li.isProper()) {
110408                 e0.addIntersections(this._li, segIndex0, 0);
110409                 e1.addIntersections(this._li, segIndex1, 1);
110410               }
110411               if (this._li.isProper()) {
110412                 this._properIntersectionPoint = this._li.getIntersection(0).copy();
110413                 this._hasProper = true;
110414                 if (this._isDoneWhenProperInt) {
110415                   this._isDone = true;
110416                 }
110417                 if (!this.isBoundaryPoint(this._li, this._bdyNodes)) { this._hasProperInterior = true; }
110418               }
110419             }
110420           }
110421         };
110422         SegmentIntersector$2.prototype.interfaces_ = function interfaces_ () {
110423           return []
110424         };
110425         SegmentIntersector$2.prototype.getClass = function getClass () {
110426           return SegmentIntersector$2
110427         };
110428         SegmentIntersector$2.isAdjacentSegments = function isAdjacentSegments (i1, i2) {
110429           return Math.abs(i1 - i2) === 1
110430         };
110431
110432         var SimpleMCSweepLineIntersector = (function (EdgeSetIntersector$$1) {
110433           function SimpleMCSweepLineIntersector () {
110434             EdgeSetIntersector$$1.call(this);
110435             this.events = new ArrayList();
110436             this.nOverlaps = null;
110437           }
110438
110439           if ( EdgeSetIntersector$$1 ) { SimpleMCSweepLineIntersector.__proto__ = EdgeSetIntersector$$1; }
110440           SimpleMCSweepLineIntersector.prototype = Object.create( EdgeSetIntersector$$1 && EdgeSetIntersector$$1.prototype );
110441           SimpleMCSweepLineIntersector.prototype.constructor = SimpleMCSweepLineIntersector;
110442           SimpleMCSweepLineIntersector.prototype.prepareEvents = function prepareEvents () {
110443             var this$1 = this;
110444
110445             Collections.sort(this.events);
110446             for (var i = 0; i < this.events.size(); i++) {
110447               var ev = this$1.events.get(i);
110448               if (ev.isDelete()) {
110449                 ev.getInsertEvent().setDeleteEventIndex(i);
110450               }
110451             }
110452           };
110453           SimpleMCSweepLineIntersector.prototype.computeIntersections = function computeIntersections () {
110454             var this$1 = this;
110455
110456             if (arguments.length === 1) {
110457               var si = arguments[0];
110458               this.nOverlaps = 0;
110459               this.prepareEvents();
110460               for (var i = 0; i < this.events.size(); i++) {
110461                 var ev = this$1.events.get(i);
110462                 if (ev.isInsert()) {
110463                   this$1.processOverlaps(i, ev.getDeleteEventIndex(), ev, si);
110464                 }
110465                 if (si.isDone()) {
110466                   break
110467                 }
110468               }
110469             } else if (arguments.length === 3) {
110470               if (arguments[2] instanceof SegmentIntersector$2 && (hasInterface(arguments[0], List) && hasInterface(arguments[1], List))) {
110471                 var edges0 = arguments[0];
110472                 var edges1 = arguments[1];
110473                 var si$1 = arguments[2];
110474                 this.addEdges(edges0, edges0);
110475                 this.addEdges(edges1, edges1);
110476                 this.computeIntersections(si$1);
110477               } else if (typeof arguments[2] === 'boolean' && (hasInterface(arguments[0], List) && arguments[1] instanceof SegmentIntersector$2)) {
110478                 var edges = arguments[0];
110479                 var si$2 = arguments[1];
110480                 var testAllSegments = arguments[2];
110481                 if (testAllSegments) { this.addEdges(edges, null); } else { this.addEdges(edges); }
110482                 this.computeIntersections(si$2);
110483               }
110484             }
110485           };
110486           SimpleMCSweepLineIntersector.prototype.addEdge = function addEdge (edge, edgeSet) {
110487             var this$1 = this;
110488
110489             var mce = edge.getMonotoneChainEdge();
110490             var startIndex = mce.getStartIndexes();
110491             for (var i = 0; i < startIndex.length - 1; i++) {
110492               var mc = new MonotoneChain$2(mce, i);
110493               var insertEvent = new SweepLineEvent(edgeSet, mce.getMinX(i), mc);
110494               this$1.events.add(insertEvent);
110495               this$1.events.add(new SweepLineEvent(mce.getMaxX(i), insertEvent));
110496             }
110497           };
110498           SimpleMCSweepLineIntersector.prototype.processOverlaps = function processOverlaps (start, end, ev0, si) {
110499             var this$1 = this;
110500
110501             var mc0 = ev0.getObject();
110502             for (var i = start; i < end; i++) {
110503               var ev1 = this$1.events.get(i);
110504               if (ev1.isInsert()) {
110505                 var mc1 = ev1.getObject();
110506                 if (!ev0.isSameLabel(ev1)) {
110507                   mc0.computeIntersections(mc1, si);
110508                   this$1.nOverlaps++;
110509                 }
110510               }
110511             }
110512           };
110513           SimpleMCSweepLineIntersector.prototype.addEdges = function addEdges () {
110514             var this$1 = this;
110515
110516             if (arguments.length === 1) {
110517               var edges = arguments[0];
110518               for (var i = edges.iterator(); i.hasNext();) {
110519                 var edge = i.next();
110520                 this$1.addEdge(edge, edge);
110521               }
110522             } else if (arguments.length === 2) {
110523               var edges$1 = arguments[0];
110524               var edgeSet = arguments[1];
110525               for (var i$1 = edges$1.iterator(); i$1.hasNext();) {
110526                 var edge$1 = i$1.next();
110527                 this$1.addEdge(edge$1, edgeSet);
110528               }
110529             }
110530           };
110531           SimpleMCSweepLineIntersector.prototype.interfaces_ = function interfaces_ () {
110532             return []
110533           };
110534           SimpleMCSweepLineIntersector.prototype.getClass = function getClass () {
110535             return SimpleMCSweepLineIntersector
110536           };
110537
110538           return SimpleMCSweepLineIntersector;
110539         }(EdgeSetIntersector));
110540
110541         var IntervalRTreeNode = function IntervalRTreeNode () {
110542           this._min = Double.POSITIVE_INFINITY;
110543           this._max = Double.NEGATIVE_INFINITY;
110544         };
110545
110546         var staticAccessors$45 = { NodeComparator: { configurable: true } };
110547         IntervalRTreeNode.prototype.getMin = function getMin () {
110548           return this._min
110549         };
110550         IntervalRTreeNode.prototype.intersects = function intersects (queryMin, queryMax) {
110551           if (this._min > queryMax || this._max < queryMin) { return false }
110552           return true
110553         };
110554         IntervalRTreeNode.prototype.getMax = function getMax () {
110555           return this._max
110556         };
110557         IntervalRTreeNode.prototype.toString = function toString () {
110558           return WKTWriter.toLineString(new Coordinate(this._min, 0), new Coordinate(this._max, 0))
110559         };
110560         IntervalRTreeNode.prototype.interfaces_ = function interfaces_ () {
110561           return []
110562         };
110563         IntervalRTreeNode.prototype.getClass = function getClass () {
110564           return IntervalRTreeNode
110565         };
110566         staticAccessors$45.NodeComparator.get = function () { return NodeComparator };
110567
110568         Object.defineProperties( IntervalRTreeNode, staticAccessors$45 );
110569
110570         var NodeComparator = function NodeComparator () {};
110571
110572         NodeComparator.prototype.compare = function compare (o1, o2) {
110573           var n1 = o1;
110574           var n2 = o2;
110575           var mid1 = (n1._min + n1._max) / 2;
110576           var mid2 = (n2._min + n2._max) / 2;
110577           if (mid1 < mid2) { return -1 }
110578           if (mid1 > mid2) { return 1 }
110579           return 0
110580         };
110581         NodeComparator.prototype.interfaces_ = function interfaces_ () {
110582           return [Comparator]
110583         };
110584         NodeComparator.prototype.getClass = function getClass () {
110585           return NodeComparator
110586         };
110587
110588         var IntervalRTreeLeafNode = (function (IntervalRTreeNode$$1) {
110589           function IntervalRTreeLeafNode () {
110590             IntervalRTreeNode$$1.call(this);
110591             this._item = null;
110592             var min = arguments[0];
110593             var max = arguments[1];
110594             var item = arguments[2];
110595             this._min = min;
110596             this._max = max;
110597             this._item = item;
110598           }
110599
110600           if ( IntervalRTreeNode$$1 ) { IntervalRTreeLeafNode.__proto__ = IntervalRTreeNode$$1; }
110601           IntervalRTreeLeafNode.prototype = Object.create( IntervalRTreeNode$$1 && IntervalRTreeNode$$1.prototype );
110602           IntervalRTreeLeafNode.prototype.constructor = IntervalRTreeLeafNode;
110603           IntervalRTreeLeafNode.prototype.query = function query (queryMin, queryMax, visitor) {
110604             if (!this.intersects(queryMin, queryMax)) { return null }
110605             visitor.visitItem(this._item);
110606           };
110607           IntervalRTreeLeafNode.prototype.interfaces_ = function interfaces_ () {
110608             return []
110609           };
110610           IntervalRTreeLeafNode.prototype.getClass = function getClass () {
110611             return IntervalRTreeLeafNode
110612           };
110613
110614           return IntervalRTreeLeafNode;
110615         }(IntervalRTreeNode));
110616
110617         var IntervalRTreeBranchNode = (function (IntervalRTreeNode$$1) {
110618           function IntervalRTreeBranchNode () {
110619             IntervalRTreeNode$$1.call(this);
110620             this._node1 = null;
110621             this._node2 = null;
110622             var n1 = arguments[0];
110623             var n2 = arguments[1];
110624             this._node1 = n1;
110625             this._node2 = n2;
110626             this.buildExtent(this._node1, this._node2);
110627           }
110628
110629           if ( IntervalRTreeNode$$1 ) { IntervalRTreeBranchNode.__proto__ = IntervalRTreeNode$$1; }
110630           IntervalRTreeBranchNode.prototype = Object.create( IntervalRTreeNode$$1 && IntervalRTreeNode$$1.prototype );
110631           IntervalRTreeBranchNode.prototype.constructor = IntervalRTreeBranchNode;
110632           IntervalRTreeBranchNode.prototype.buildExtent = function buildExtent (n1, n2) {
110633             this._min = Math.min(n1._min, n2._min);
110634             this._max = Math.max(n1._max, n2._max);
110635           };
110636           IntervalRTreeBranchNode.prototype.query = function query (queryMin, queryMax, visitor) {
110637             if (!this.intersects(queryMin, queryMax)) {
110638               return null
110639             }
110640             if (this._node1 !== null) { this._node1.query(queryMin, queryMax, visitor); }
110641             if (this._node2 !== null) { this._node2.query(queryMin, queryMax, visitor); }
110642           };
110643           IntervalRTreeBranchNode.prototype.interfaces_ = function interfaces_ () {
110644             return []
110645           };
110646           IntervalRTreeBranchNode.prototype.getClass = function getClass () {
110647             return IntervalRTreeBranchNode
110648           };
110649
110650           return IntervalRTreeBranchNode;
110651         }(IntervalRTreeNode));
110652
110653         var SortedPackedIntervalRTree = function SortedPackedIntervalRTree () {
110654           this._leaves = new ArrayList();
110655           this._root = null;
110656           this._level = 0;
110657         };
110658         SortedPackedIntervalRTree.prototype.buildTree = function buildTree () {
110659             var this$1 = this;
110660
110661           Collections.sort(this._leaves, new IntervalRTreeNode.NodeComparator());
110662           var src = this._leaves;
110663           var temp = null;
110664           var dest = new ArrayList();
110665           while (true) {
110666             this$1.buildLevel(src, dest);
110667             if (dest.size() === 1) { return dest.get(0) }
110668             temp = src;
110669             src = dest;
110670             dest = temp;
110671           }
110672         };
110673         SortedPackedIntervalRTree.prototype.insert = function insert (min, max, item) {
110674           if (this._root !== null) { throw new Error('Index cannot be added to once it has been queried') }
110675           this._leaves.add(new IntervalRTreeLeafNode(min, max, item));
110676         };
110677         SortedPackedIntervalRTree.prototype.query = function query (min, max, visitor) {
110678           this.init();
110679           this._root.query(min, max, visitor);
110680         };
110681         SortedPackedIntervalRTree.prototype.buildRoot = function buildRoot () {
110682           if (this._root !== null) { return null }
110683           this._root = this.buildTree();
110684         };
110685         SortedPackedIntervalRTree.prototype.printNode = function printNode (node) {
110686           System.out.println(WKTWriter.toLineString(new Coordinate(node._min, this._level), new Coordinate(node._max, this._level)));
110687         };
110688         SortedPackedIntervalRTree.prototype.init = function init () {
110689           if (this._root !== null) { return null }
110690           this.buildRoot();
110691         };
110692         SortedPackedIntervalRTree.prototype.buildLevel = function buildLevel (src, dest) {
110693           this._level++;
110694           dest.clear();
110695           for (var i = 0; i < src.size(); i += 2) {
110696             var n1 = src.get(i);
110697             var n2 = i + 1 < src.size() ? src.get(i) : null;
110698             if (n2 === null) {
110699               dest.add(n1);
110700             } else {
110701               var node = new IntervalRTreeBranchNode(src.get(i), src.get(i + 1));
110702               dest.add(node);
110703             }
110704           }
110705         };
110706         SortedPackedIntervalRTree.prototype.interfaces_ = function interfaces_ () {
110707           return []
110708         };
110709         SortedPackedIntervalRTree.prototype.getClass = function getClass () {
110710           return SortedPackedIntervalRTree
110711         };
110712
110713         var ArrayListVisitor = function ArrayListVisitor () {
110714           this._items = new ArrayList();
110715         };
110716         ArrayListVisitor.prototype.visitItem = function visitItem (item) {
110717           this._items.add(item);
110718         };
110719         ArrayListVisitor.prototype.getItems = function getItems () {
110720           return this._items
110721         };
110722         ArrayListVisitor.prototype.interfaces_ = function interfaces_ () {
110723           return [ItemVisitor]
110724         };
110725         ArrayListVisitor.prototype.getClass = function getClass () {
110726           return ArrayListVisitor
110727         };
110728
110729         var IndexedPointInAreaLocator = function IndexedPointInAreaLocator () {
110730           this._index = null;
110731           var g = arguments[0];
110732           if (!hasInterface(g, Polygonal)) { throw new IllegalArgumentException('Argument must be Polygonal') }
110733           this._index = new IntervalIndexedGeometry(g);
110734         };
110735
110736         var staticAccessors$44 = { SegmentVisitor: { configurable: true },IntervalIndexedGeometry: { configurable: true } };
110737         IndexedPointInAreaLocator.prototype.locate = function locate (p) {
110738           var rcc = new RayCrossingCounter(p);
110739           var visitor = new SegmentVisitor(rcc);
110740           this._index.query(p.y, p.y, visitor);
110741           return rcc.getLocation()
110742         };
110743         IndexedPointInAreaLocator.prototype.interfaces_ = function interfaces_ () {
110744           return [PointOnGeometryLocator]
110745         };
110746         IndexedPointInAreaLocator.prototype.getClass = function getClass () {
110747           return IndexedPointInAreaLocator
110748         };
110749         staticAccessors$44.SegmentVisitor.get = function () { return SegmentVisitor };
110750         staticAccessors$44.IntervalIndexedGeometry.get = function () { return IntervalIndexedGeometry };
110751
110752         Object.defineProperties( IndexedPointInAreaLocator, staticAccessors$44 );
110753
110754         var SegmentVisitor = function SegmentVisitor () {
110755           this._counter = null;
110756           var counter = arguments[0];
110757           this._counter = counter;
110758         };
110759         SegmentVisitor.prototype.visitItem = function visitItem (item) {
110760           var seg = item;
110761           this._counter.countSegment(seg.getCoordinate(0), seg.getCoordinate(1));
110762         };
110763         SegmentVisitor.prototype.interfaces_ = function interfaces_ () {
110764           return [ItemVisitor]
110765         };
110766         SegmentVisitor.prototype.getClass = function getClass () {
110767           return SegmentVisitor
110768         };
110769
110770         var IntervalIndexedGeometry = function IntervalIndexedGeometry () {
110771           this._index = new SortedPackedIntervalRTree();
110772           var geom = arguments[0];
110773           this.init(geom);
110774         };
110775         IntervalIndexedGeometry.prototype.init = function init (geom) {
110776             var this$1 = this;
110777
110778           var lines = LinearComponentExtracter.getLines(geom);
110779           for (var i = lines.iterator(); i.hasNext();) {
110780             var line = i.next();
110781             var pts = line.getCoordinates();
110782             this$1.addLine(pts);
110783           }
110784         };
110785         IntervalIndexedGeometry.prototype.addLine = function addLine (pts) {
110786             var this$1 = this;
110787
110788           for (var i = 1; i < pts.length; i++) {
110789             var seg = new LineSegment(pts[i - 1], pts[i]);
110790             var min = Math.min(seg.p0.y, seg.p1.y);
110791             var max = Math.max(seg.p0.y, seg.p1.y);
110792             this$1._index.insert(min, max, seg);
110793           }
110794         };
110795         IntervalIndexedGeometry.prototype.query = function query () {
110796           if (arguments.length === 2) {
110797             var min = arguments[0];
110798             var max = arguments[1];
110799             var visitor = new ArrayListVisitor();
110800             this._index.query(min, max, visitor);
110801             return visitor.getItems()
110802           } else if (arguments.length === 3) {
110803             var min$1 = arguments[0];
110804             var max$1 = arguments[1];
110805             var visitor$1 = arguments[2];
110806             this._index.query(min$1, max$1, visitor$1);
110807           }
110808         };
110809         IntervalIndexedGeometry.prototype.interfaces_ = function interfaces_ () {
110810           return []
110811         };
110812         IntervalIndexedGeometry.prototype.getClass = function getClass () {
110813           return IntervalIndexedGeometry
110814         };
110815
110816         var GeometryGraph = (function (PlanarGraph$$1) {
110817           function GeometryGraph () {
110818             PlanarGraph$$1.call(this);
110819             this._parentGeom = null;
110820             this._lineEdgeMap = new HashMap();
110821             this._boundaryNodeRule = null;
110822             this._useBoundaryDeterminationRule = true;
110823             this._argIndex = null;
110824             this._boundaryNodes = null;
110825             this._hasTooFewPoints = false;
110826             this._invalidPoint = null;
110827             this._areaPtLocator = null;
110828             this._ptLocator = new PointLocator();
110829             if (arguments.length === 2) {
110830               var argIndex = arguments[0];
110831               var parentGeom = arguments[1];
110832               var boundaryNodeRule = BoundaryNodeRule.OGC_SFS_BOUNDARY_RULE;
110833               this._argIndex = argIndex;
110834               this._parentGeom = parentGeom;
110835               this._boundaryNodeRule = boundaryNodeRule;
110836               if (parentGeom !== null) {
110837                 this.add(parentGeom);
110838               }
110839             } else if (arguments.length === 3) {
110840               var argIndex$1 = arguments[0];
110841               var parentGeom$1 = arguments[1];
110842               var boundaryNodeRule$1 = arguments[2];
110843               this._argIndex = argIndex$1;
110844               this._parentGeom = parentGeom$1;
110845               this._boundaryNodeRule = boundaryNodeRule$1;
110846               if (parentGeom$1 !== null) {
110847                 this.add(parentGeom$1);
110848               }
110849             }
110850           }
110851
110852           if ( PlanarGraph$$1 ) { GeometryGraph.__proto__ = PlanarGraph$$1; }
110853           GeometryGraph.prototype = Object.create( PlanarGraph$$1 && PlanarGraph$$1.prototype );
110854           GeometryGraph.prototype.constructor = GeometryGraph;
110855           GeometryGraph.prototype.insertBoundaryPoint = function insertBoundaryPoint (argIndex, coord) {
110856             var n = this._nodes.addNode(coord);
110857             var lbl = n.getLabel();
110858             var boundaryCount = 1;
110859             var loc = Location.NONE;
110860             loc = lbl.getLocation(argIndex, Position.ON);
110861             if (loc === Location.BOUNDARY) { boundaryCount++; }
110862             var newLoc = GeometryGraph.determineBoundary(this._boundaryNodeRule, boundaryCount);
110863             lbl.setLocation(argIndex, newLoc);
110864           };
110865           GeometryGraph.prototype.computeSelfNodes = function computeSelfNodes () {
110866             if (arguments.length === 2) {
110867               var li = arguments[0];
110868               var computeRingSelfNodes = arguments[1];
110869               return this.computeSelfNodes(li, computeRingSelfNodes, false)
110870             } else if (arguments.length === 3) {
110871               var li$1 = arguments[0];
110872               var computeRingSelfNodes$1 = arguments[1];
110873               var isDoneIfProperInt = arguments[2];
110874               var si = new SegmentIntersector$2(li$1, true, false);
110875               si.setIsDoneIfProperInt(isDoneIfProperInt);
110876               var esi = this.createEdgeSetIntersector();
110877               var isRings = this._parentGeom instanceof LinearRing || this._parentGeom instanceof Polygon || this._parentGeom instanceof MultiPolygon;
110878               var computeAllSegments = computeRingSelfNodes$1 || !isRings;
110879               esi.computeIntersections(this._edges, si, computeAllSegments);
110880               this.addSelfIntersectionNodes(this._argIndex);
110881               return si
110882             }
110883           };
110884           GeometryGraph.prototype.computeSplitEdges = function computeSplitEdges (edgelist) {
110885             for (var i = this._edges.iterator(); i.hasNext();) {
110886               var e = i.next();
110887               e.eiList.addSplitEdges(edgelist);
110888             }
110889           };
110890           GeometryGraph.prototype.computeEdgeIntersections = function computeEdgeIntersections (g, li, includeProper) {
110891             var si = new SegmentIntersector$2(li, includeProper, true);
110892             si.setBoundaryNodes(this.getBoundaryNodes(), g.getBoundaryNodes());
110893             var esi = this.createEdgeSetIntersector();
110894             esi.computeIntersections(this._edges, g._edges, si);
110895             return si
110896           };
110897           GeometryGraph.prototype.getGeometry = function getGeometry () {
110898             return this._parentGeom
110899           };
110900           GeometryGraph.prototype.getBoundaryNodeRule = function getBoundaryNodeRule () {
110901             return this._boundaryNodeRule
110902           };
110903           GeometryGraph.prototype.hasTooFewPoints = function hasTooFewPoints () {
110904             return this._hasTooFewPoints
110905           };
110906           GeometryGraph.prototype.addPoint = function addPoint () {
110907             if (arguments[0] instanceof Point$1) {
110908               var p = arguments[0];
110909               var coord = p.getCoordinate();
110910               this.insertPoint(this._argIndex, coord, Location.INTERIOR);
110911             } else if (arguments[0] instanceof Coordinate) {
110912               var pt = arguments[0];
110913               this.insertPoint(this._argIndex, pt, Location.INTERIOR);
110914             }
110915           };
110916           GeometryGraph.prototype.addPolygon = function addPolygon (p) {
110917             var this$1 = this;
110918
110919             this.addPolygonRing(p.getExteriorRing(), Location.EXTERIOR, Location.INTERIOR);
110920             for (var i = 0; i < p.getNumInteriorRing(); i++) {
110921               var hole = p.getInteriorRingN(i);
110922               this$1.addPolygonRing(hole, Location.INTERIOR, Location.EXTERIOR);
110923             }
110924           };
110925           GeometryGraph.prototype.addEdge = function addEdge (e) {
110926             this.insertEdge(e);
110927             var coord = e.getCoordinates();
110928             this.insertPoint(this._argIndex, coord[0], Location.BOUNDARY);
110929             this.insertPoint(this._argIndex, coord[coord.length - 1], Location.BOUNDARY);
110930           };
110931           GeometryGraph.prototype.addLineString = function addLineString (line) {
110932             var coord = CoordinateArrays.removeRepeatedPoints(line.getCoordinates());
110933             if (coord.length < 2) {
110934               this._hasTooFewPoints = true;
110935               this._invalidPoint = coord[0];
110936               return null
110937             }
110938             var e = new Edge(coord, new Label(this._argIndex, Location.INTERIOR));
110939             this._lineEdgeMap.put(line, e);
110940             this.insertEdge(e);
110941             Assert.isTrue(coord.length >= 2, 'found LineString with single point');
110942             this.insertBoundaryPoint(this._argIndex, coord[0]);
110943             this.insertBoundaryPoint(this._argIndex, coord[coord.length - 1]);
110944           };
110945           GeometryGraph.prototype.getInvalidPoint = function getInvalidPoint () {
110946             return this._invalidPoint
110947           };
110948           GeometryGraph.prototype.getBoundaryPoints = function getBoundaryPoints () {
110949             var coll = this.getBoundaryNodes();
110950             var pts = new Array(coll.size()).fill(null);
110951             var i = 0;
110952             for (var it = coll.iterator(); it.hasNext();) {
110953               var node = it.next();
110954               pts[i++] = node.getCoordinate().copy();
110955             }
110956             return pts
110957           };
110958           GeometryGraph.prototype.getBoundaryNodes = function getBoundaryNodes () {
110959             if (this._boundaryNodes === null) { this._boundaryNodes = this._nodes.getBoundaryNodes(this._argIndex); }
110960             return this._boundaryNodes
110961           };
110962           GeometryGraph.prototype.addSelfIntersectionNode = function addSelfIntersectionNode (argIndex, coord, loc) {
110963             if (this.isBoundaryNode(argIndex, coord)) { return null }
110964             if (loc === Location.BOUNDARY && this._useBoundaryDeterminationRule) { this.insertBoundaryPoint(argIndex, coord); } else { this.insertPoint(argIndex, coord, loc); }
110965           };
110966           GeometryGraph.prototype.addPolygonRing = function addPolygonRing (lr, cwLeft, cwRight) {
110967             if (lr.isEmpty()) { return null }
110968             var coord = CoordinateArrays.removeRepeatedPoints(lr.getCoordinates());
110969             if (coord.length < 4) {
110970               this._hasTooFewPoints = true;
110971               this._invalidPoint = coord[0];
110972               return null
110973             }
110974             var left = cwLeft;
110975             var right = cwRight;
110976             if (CGAlgorithms.isCCW(coord)) {
110977               left = cwRight;
110978               right = cwLeft;
110979             }
110980             var e = new Edge(coord, new Label(this._argIndex, Location.BOUNDARY, left, right));
110981             this._lineEdgeMap.put(lr, e);
110982             this.insertEdge(e);
110983             this.insertPoint(this._argIndex, coord[0], Location.BOUNDARY);
110984           };
110985           GeometryGraph.prototype.insertPoint = function insertPoint (argIndex, coord, onLocation) {
110986             var n = this._nodes.addNode(coord);
110987             var lbl = n.getLabel();
110988             if (lbl === null) {
110989               n._label = new Label(argIndex, onLocation);
110990             } else { lbl.setLocation(argIndex, onLocation); }
110991           };
110992           GeometryGraph.prototype.createEdgeSetIntersector = function createEdgeSetIntersector () {
110993             return new SimpleMCSweepLineIntersector()
110994           };
110995           GeometryGraph.prototype.addSelfIntersectionNodes = function addSelfIntersectionNodes (argIndex) {
110996             var this$1 = this;
110997
110998             for (var i = this._edges.iterator(); i.hasNext();) {
110999               var e = i.next();
111000               var eLoc = e.getLabel().getLocation(argIndex);
111001               for (var eiIt = e.eiList.iterator(); eiIt.hasNext();) {
111002                 var ei = eiIt.next();
111003                 this$1.addSelfIntersectionNode(argIndex, ei.coord, eLoc);
111004               }
111005             }
111006           };
111007           GeometryGraph.prototype.add = function add () {
111008             if (arguments.length === 1) {
111009               var g = arguments[0];
111010               if (g.isEmpty()) { return null }
111011               if (g instanceof MultiPolygon) { this._useBoundaryDeterminationRule = false; }
111012               if (g instanceof Polygon) { this.addPolygon(g); }
111013               else if (g instanceof LineString) { this.addLineString(g); }
111014               else if (g instanceof Point$1) { this.addPoint(g); }
111015               else if (g instanceof MultiPoint) { this.addCollection(g); }
111016               else if (g instanceof MultiLineString) { this.addCollection(g); }
111017               else if (g instanceof MultiPolygon) { this.addCollection(g); }
111018               else if (g instanceof GeometryCollection) { this.addCollection(g); }
111019               else { throw new Error(g.getClass().getName()) }
111020             } else { return PlanarGraph$$1.prototype.add.apply(this, arguments) }
111021           };
111022           GeometryGraph.prototype.addCollection = function addCollection (gc) {
111023             var this$1 = this;
111024
111025             for (var i = 0; i < gc.getNumGeometries(); i++) {
111026               var g = gc.getGeometryN(i);
111027               this$1.add(g);
111028             }
111029           };
111030           GeometryGraph.prototype.locate = function locate (pt) {
111031             if (hasInterface(this._parentGeom, Polygonal) && this._parentGeom.getNumGeometries() > 50) {
111032               if (this._areaPtLocator === null) {
111033                 this._areaPtLocator = new IndexedPointInAreaLocator(this._parentGeom);
111034               }
111035               return this._areaPtLocator.locate(pt)
111036             }
111037             return this._ptLocator.locate(pt, this._parentGeom)
111038           };
111039           GeometryGraph.prototype.findEdge = function findEdge () {
111040             if (arguments.length === 1) {
111041               var line = arguments[0];
111042               return this._lineEdgeMap.get(line)
111043             } else { return PlanarGraph$$1.prototype.findEdge.apply(this, arguments) }
111044           };
111045           GeometryGraph.prototype.interfaces_ = function interfaces_ () {
111046             return []
111047           };
111048           GeometryGraph.prototype.getClass = function getClass () {
111049             return GeometryGraph
111050           };
111051           GeometryGraph.determineBoundary = function determineBoundary (boundaryNodeRule, boundaryCount) {
111052             return boundaryNodeRule.isInBoundary(boundaryCount) ? Location.BOUNDARY : Location.INTERIOR
111053           };
111054
111055           return GeometryGraph;
111056         }(PlanarGraph));
111057
111058         var GeometryGraphOp = function GeometryGraphOp () {
111059           this._li = new RobustLineIntersector();
111060           this._resultPrecisionModel = null;
111061           this._arg = null;
111062           if (arguments.length === 1) {
111063             var g0 = arguments[0];
111064             this.setComputationPrecision(g0.getPrecisionModel());
111065             this._arg = new Array(1).fill(null);
111066             this._arg[0] = new GeometryGraph(0, g0);
111067           } else if (arguments.length === 2) {
111068             var g0$1 = arguments[0];
111069             var g1 = arguments[1];
111070             var boundaryNodeRule = BoundaryNodeRule.OGC_SFS_BOUNDARY_RULE;
111071             if (g0$1.getPrecisionModel().compareTo(g1.getPrecisionModel()) >= 0) { this.setComputationPrecision(g0$1.getPrecisionModel()); } else { this.setComputationPrecision(g1.getPrecisionModel()); }
111072             this._arg = new Array(2).fill(null);
111073             this._arg[0] = new GeometryGraph(0, g0$1, boundaryNodeRule);
111074             this._arg[1] = new GeometryGraph(1, g1, boundaryNodeRule);
111075           } else if (arguments.length === 3) {
111076             var g0$2 = arguments[0];
111077             var g1$1 = arguments[1];
111078             var boundaryNodeRule$1 = arguments[2];
111079             if (g0$2.getPrecisionModel().compareTo(g1$1.getPrecisionModel()) >= 0) { this.setComputationPrecision(g0$2.getPrecisionModel()); } else { this.setComputationPrecision(g1$1.getPrecisionModel()); }
111080             this._arg = new Array(2).fill(null);
111081             this._arg[0] = new GeometryGraph(0, g0$2, boundaryNodeRule$1);
111082             this._arg[1] = new GeometryGraph(1, g1$1, boundaryNodeRule$1);
111083           }
111084         };
111085         GeometryGraphOp.prototype.getArgGeometry = function getArgGeometry (i) {
111086           return this._arg[i].getGeometry()
111087         };
111088         GeometryGraphOp.prototype.setComputationPrecision = function setComputationPrecision (pm) {
111089           this._resultPrecisionModel = pm;
111090           this._li.setPrecisionModel(this._resultPrecisionModel);
111091         };
111092         GeometryGraphOp.prototype.interfaces_ = function interfaces_ () {
111093           return []
111094         };
111095         GeometryGraphOp.prototype.getClass = function getClass () {
111096           return GeometryGraphOp
111097         };
111098
111099         // operation.geometrygraph
111100
111101         var GeometryMapper = function GeometryMapper () {};
111102
111103         GeometryMapper.prototype.interfaces_ = function interfaces_ () {
111104           return []
111105         };
111106         GeometryMapper.prototype.getClass = function getClass () {
111107           return GeometryMapper
111108         };
111109         GeometryMapper.map = function map () {
111110           if (arguments[0] instanceof Geometry && hasInterface(arguments[1], GeometryMapper.MapOp)) {
111111             var geom = arguments[0];
111112             var op = arguments[1];
111113             var mapped = new ArrayList();
111114             for (var i = 0; i < geom.getNumGeometries(); i++) {
111115               var g = op.map(geom.getGeometryN(i));
111116               if (g !== null) { mapped.add(g); }
111117             }
111118             return geom.getFactory().buildGeometry(mapped)
111119           } else if (hasInterface(arguments[0], Collection) && hasInterface(arguments[1], GeometryMapper.MapOp)) {
111120             var geoms = arguments[0];
111121             var op$1 = arguments[1];
111122             var mapped$1 = new ArrayList();
111123             for (var i$1 = geoms.iterator(); i$1.hasNext();) {
111124               var g$1 = i$1.next();
111125               var gr = op$1.map(g$1);
111126               if (gr !== null) { mapped$1.add(gr); }
111127             }
111128             return mapped$1
111129           }
111130         };
111131         GeometryMapper.MapOp = function MapOp () {};
111132
111133         var OverlayOp = (function (GeometryGraphOp) {
111134           function OverlayOp () {
111135             var g0 = arguments[0];
111136             var g1 = arguments[1];
111137             GeometryGraphOp.call(this, g0, g1);
111138             this._ptLocator = new PointLocator();
111139             this._geomFact = null;
111140             this._resultGeom = null;
111141             this._graph = null;
111142             this._edgeList = new EdgeList();
111143             this._resultPolyList = new ArrayList();
111144             this._resultLineList = new ArrayList();
111145             this._resultPointList = new ArrayList();
111146             this._graph = new PlanarGraph(new OverlayNodeFactory());
111147             this._geomFact = g0.getFactory();
111148           }
111149
111150           if ( GeometryGraphOp ) { OverlayOp.__proto__ = GeometryGraphOp; }
111151           OverlayOp.prototype = Object.create( GeometryGraphOp && GeometryGraphOp.prototype );
111152           OverlayOp.prototype.constructor = OverlayOp;
111153           OverlayOp.prototype.insertUniqueEdge = function insertUniqueEdge (e) {
111154             var existingEdge = this._edgeList.findEqualEdge(e);
111155             if (existingEdge !== null) {
111156               var existingLabel = existingEdge.getLabel();
111157               var labelToMerge = e.getLabel();
111158               if (!existingEdge.isPointwiseEqual(e)) {
111159                 labelToMerge = new Label(e.getLabel());
111160                 labelToMerge.flip();
111161               }
111162               var depth = existingEdge.getDepth();
111163               if (depth.isNull()) {
111164                 depth.add(existingLabel);
111165               }
111166               depth.add(labelToMerge);
111167               existingLabel.merge(labelToMerge);
111168             } else {
111169               this._edgeList.add(e);
111170             }
111171           };
111172           OverlayOp.prototype.getGraph = function getGraph () {
111173             return this._graph
111174           };
111175           OverlayOp.prototype.cancelDuplicateResultEdges = function cancelDuplicateResultEdges () {
111176             for (var it = this._graph.getEdgeEnds().iterator(); it.hasNext();) {
111177               var de = it.next();
111178               var sym = de.getSym();
111179               if (de.isInResult() && sym.isInResult()) {
111180                 de.setInResult(false);
111181                 sym.setInResult(false);
111182               }
111183             }
111184           };
111185           OverlayOp.prototype.isCoveredByLA = function isCoveredByLA (coord) {
111186             if (this.isCovered(coord, this._resultLineList)) { return true }
111187             if (this.isCovered(coord, this._resultPolyList)) { return true }
111188             return false
111189           };
111190           OverlayOp.prototype.computeGeometry = function computeGeometry (resultPointList, resultLineList, resultPolyList, opcode) {
111191             var geomList = new ArrayList();
111192             geomList.addAll(resultPointList);
111193             geomList.addAll(resultLineList);
111194             geomList.addAll(resultPolyList);
111195             if (geomList.isEmpty()) { return OverlayOp.createEmptyResult(opcode, this._arg[0].getGeometry(), this._arg[1].getGeometry(), this._geomFact) }
111196             return this._geomFact.buildGeometry(geomList)
111197           };
111198           OverlayOp.prototype.mergeSymLabels = function mergeSymLabels () {
111199             for (var nodeit = this._graph.getNodes().iterator(); nodeit.hasNext();) {
111200               var node = nodeit.next();
111201               node.getEdges().mergeSymLabels();
111202             }
111203           };
111204           OverlayOp.prototype.isCovered = function isCovered (coord, geomList) {
111205             var this$1 = this;
111206
111207             for (var it = geomList.iterator(); it.hasNext();) {
111208               var geom = it.next();
111209               var loc = this$1._ptLocator.locate(coord, geom);
111210               if (loc !== Location.EXTERIOR) { return true }
111211             }
111212             return false
111213           };
111214           OverlayOp.prototype.replaceCollapsedEdges = function replaceCollapsedEdges () {
111215             var newEdges = new ArrayList();
111216             for (var it = this._edgeList.iterator(); it.hasNext();) {
111217               var e = it.next();
111218               if (e.isCollapsed()) {
111219                 it.remove();
111220                 newEdges.add(e.getCollapsedEdge());
111221               }
111222             }
111223             this._edgeList.addAll(newEdges);
111224           };
111225           OverlayOp.prototype.updateNodeLabelling = function updateNodeLabelling () {
111226             for (var nodeit = this._graph.getNodes().iterator(); nodeit.hasNext();) {
111227               var node = nodeit.next();
111228               var lbl = node.getEdges().getLabel();
111229               node.getLabel().merge(lbl);
111230             }
111231           };
111232           OverlayOp.prototype.getResultGeometry = function getResultGeometry (overlayOpCode) {
111233             this.computeOverlay(overlayOpCode);
111234             return this._resultGeom
111235           };
111236           OverlayOp.prototype.insertUniqueEdges = function insertUniqueEdges (edges) {
111237             var this$1 = this;
111238
111239             for (var i = edges.iterator(); i.hasNext();) {
111240               var e = i.next();
111241               this$1.insertUniqueEdge(e);
111242             }
111243           };
111244           OverlayOp.prototype.computeOverlay = function computeOverlay (opCode) {
111245             this.copyPoints(0);
111246             this.copyPoints(1);
111247             this._arg[0].computeSelfNodes(this._li, false);
111248             this._arg[1].computeSelfNodes(this._li, false);
111249             this._arg[0].computeEdgeIntersections(this._arg[1], this._li, true);
111250             var baseSplitEdges = new ArrayList();
111251             this._arg[0].computeSplitEdges(baseSplitEdges);
111252             this._arg[1].computeSplitEdges(baseSplitEdges);
111253             // const splitEdges = baseSplitEdges
111254             this.insertUniqueEdges(baseSplitEdges);
111255             this.computeLabelsFromDepths();
111256             this.replaceCollapsedEdges();
111257             EdgeNodingValidator.checkValid(this._edgeList.getEdges());
111258             this._graph.addEdges(this._edgeList.getEdges());
111259             this.computeLabelling();
111260             this.labelIncompleteNodes();
111261             this.findResultAreaEdges(opCode);
111262             this.cancelDuplicateResultEdges();
111263             var polyBuilder = new PolygonBuilder(this._geomFact);
111264             polyBuilder.add(this._graph);
111265             this._resultPolyList = polyBuilder.getPolygons();
111266             var lineBuilder = new LineBuilder(this, this._geomFact, this._ptLocator);
111267             this._resultLineList = lineBuilder.build(opCode);
111268             var pointBuilder = new PointBuilder(this, this._geomFact, this._ptLocator);
111269             this._resultPointList = pointBuilder.build(opCode);
111270             this._resultGeom = this.computeGeometry(this._resultPointList, this._resultLineList, this._resultPolyList, opCode);
111271           };
111272           OverlayOp.prototype.labelIncompleteNode = function labelIncompleteNode (n, targetIndex) {
111273             var loc = this._ptLocator.locate(n.getCoordinate(), this._arg[targetIndex].getGeometry());
111274             n.getLabel().setLocation(targetIndex, loc);
111275           };
111276           OverlayOp.prototype.copyPoints = function copyPoints (argIndex) {
111277             var this$1 = this;
111278
111279             for (var i = this._arg[argIndex].getNodeIterator(); i.hasNext();) {
111280               var graphNode = i.next();
111281               var newNode = this$1._graph.addNode(graphNode.getCoordinate());
111282               newNode.setLabel(argIndex, graphNode.getLabel().getLocation(argIndex));
111283             }
111284           };
111285           OverlayOp.prototype.findResultAreaEdges = function findResultAreaEdges (opCode) {
111286             for (var it = this._graph.getEdgeEnds().iterator(); it.hasNext();) {
111287               var de = it.next();
111288               var label = de.getLabel();
111289               if (label.isArea() && !de.isInteriorAreaEdge() && OverlayOp.isResultOfOp(label.getLocation(0, Position.RIGHT), label.getLocation(1, Position.RIGHT), opCode)) {
111290                 de.setInResult(true);
111291               }
111292             }
111293           };
111294           OverlayOp.prototype.computeLabelsFromDepths = function computeLabelsFromDepths () {
111295             for (var it = this._edgeList.iterator(); it.hasNext();) {
111296               var e = it.next();
111297               var lbl = e.getLabel();
111298               var depth = e.getDepth();
111299               if (!depth.isNull()) {
111300                 depth.normalize();
111301                 for (var i = 0; i < 2; i++) {
111302                   if (!lbl.isNull(i) && lbl.isArea() && !depth.isNull(i)) {
111303                     if (depth.getDelta(i) === 0) {
111304                       lbl.toLine(i);
111305                     } else {
111306                       Assert.isTrue(!depth.isNull(i, Position.LEFT), 'depth of LEFT side has not been initialized');
111307                       lbl.setLocation(i, Position.LEFT, depth.getLocation(i, Position.LEFT));
111308                       Assert.isTrue(!depth.isNull(i, Position.RIGHT), 'depth of RIGHT side has not been initialized');
111309                       lbl.setLocation(i, Position.RIGHT, depth.getLocation(i, Position.RIGHT));
111310                     }
111311                   }
111312                 }
111313               }
111314             }
111315           };
111316           OverlayOp.prototype.computeLabelling = function computeLabelling () {
111317             var this$1 = this;
111318
111319             for (var nodeit = this._graph.getNodes().iterator(); nodeit.hasNext();) {
111320               var node = nodeit.next();
111321               node.getEdges().computeLabelling(this$1._arg);
111322             }
111323             this.mergeSymLabels();
111324             this.updateNodeLabelling();
111325           };
111326           OverlayOp.prototype.labelIncompleteNodes = function labelIncompleteNodes () {
111327             var this$1 = this;
111328
111329             // let nodeCount = 0
111330             for (var ni = this._graph.getNodes().iterator(); ni.hasNext();) {
111331               var n = ni.next();
111332               var label = n.getLabel();
111333               if (n.isIsolated()) {
111334                 // nodeCount++
111335                 if (label.isNull(0)) { this$1.labelIncompleteNode(n, 0); } else { this$1.labelIncompleteNode(n, 1); }
111336               }
111337               n.getEdges().updateLabelling(label);
111338             }
111339           };
111340           OverlayOp.prototype.isCoveredByA = function isCoveredByA (coord) {
111341             if (this.isCovered(coord, this._resultPolyList)) { return true }
111342             return false
111343           };
111344           OverlayOp.prototype.interfaces_ = function interfaces_ () {
111345             return []
111346           };
111347           OverlayOp.prototype.getClass = function getClass () {
111348             return OverlayOp
111349           };
111350
111351           return OverlayOp;
111352         }(GeometryGraphOp));
111353
111354         OverlayOp.overlayOp = function (geom0, geom1, opCode) {
111355           var gov = new OverlayOp(geom0, geom1);
111356           var geomOv = gov.getResultGeometry(opCode);
111357           return geomOv
111358         };
111359         OverlayOp.intersection = function (g, other) {
111360           if (g.isEmpty() || other.isEmpty()) { return OverlayOp.createEmptyResult(OverlayOp.INTERSECTION, g, other, g.getFactory()) }
111361           if (g.isGeometryCollection()) {
111362             var g2 = other;
111363             return GeometryCollectionMapper.map(g, {
111364               interfaces_: function () {
111365                 return [GeometryMapper.MapOp]
111366               },
111367               map: function (g) {
111368                 return g.intersection(g2)
111369               }
111370             })
111371           }
111372           g.checkNotGeometryCollection(g);
111373           g.checkNotGeometryCollection(other);
111374           return SnapIfNeededOverlayOp.overlayOp(g, other, OverlayOp.INTERSECTION)
111375         };
111376         OverlayOp.symDifference = function (g, other) {
111377           if (g.isEmpty() || other.isEmpty()) {
111378             if (g.isEmpty() && other.isEmpty()) { return OverlayOp.createEmptyResult(OverlayOp.SYMDIFFERENCE, g, other, g.getFactory()) }
111379             if (g.isEmpty()) { return other.copy() }
111380             if (other.isEmpty()) { return g.copy() }
111381           }
111382           g.checkNotGeometryCollection(g);
111383           g.checkNotGeometryCollection(other);
111384           return SnapIfNeededOverlayOp.overlayOp(g, other, OverlayOp.SYMDIFFERENCE)
111385         };
111386         OverlayOp.resultDimension = function (opCode, g0, g1) {
111387           var dim0 = g0.getDimension();
111388           var dim1 = g1.getDimension();
111389           var resultDimension = -1;
111390           switch (opCode) {
111391             case OverlayOp.INTERSECTION:
111392               resultDimension = Math.min(dim0, dim1);
111393               break
111394             case OverlayOp.UNION:
111395               resultDimension = Math.max(dim0, dim1);
111396               break
111397             case OverlayOp.DIFFERENCE:
111398               resultDimension = dim0;
111399               break
111400             case OverlayOp.SYMDIFFERENCE:
111401               resultDimension = Math.max(dim0, dim1);
111402               break
111403           }
111404           return resultDimension
111405         };
111406         OverlayOp.createEmptyResult = function (overlayOpCode, a, b, geomFact) {
111407           var result = null;
111408           switch (OverlayOp.resultDimension(overlayOpCode, a, b)) {
111409             case -1:
111410               result = geomFact.createGeometryCollection(new Array(0).fill(null));
111411               break
111412             case 0:
111413               result = geomFact.createPoint();
111414               break
111415             case 1:
111416               result = geomFact.createLineString();
111417               break
111418             case 2:
111419               result = geomFact.createPolygon();
111420               break
111421           }
111422           return result
111423         };
111424         OverlayOp.difference = function (g, other) {
111425           if (g.isEmpty()) { return OverlayOp.createEmptyResult(OverlayOp.DIFFERENCE, g, other, g.getFactory()) }
111426           if (other.isEmpty()) { return g.copy() }
111427           g.checkNotGeometryCollection(g);
111428           g.checkNotGeometryCollection(other);
111429           return SnapIfNeededOverlayOp.overlayOp(g, other, OverlayOp.DIFFERENCE)
111430         };
111431         OverlayOp.isResultOfOp = function () {
111432           if (arguments.length === 2) {
111433             var label = arguments[0];
111434             var opCode = arguments[1];
111435             var loc0 = label.getLocation(0);
111436             var loc1 = label.getLocation(1);
111437             return OverlayOp.isResultOfOp(loc0, loc1, opCode)
111438           } else if (arguments.length === 3) {
111439             var loc0$1 = arguments[0];
111440             var loc1$1 = arguments[1];
111441             var overlayOpCode = arguments[2];
111442             if (loc0$1 === Location.BOUNDARY) { loc0$1 = Location.INTERIOR; }
111443             if (loc1$1 === Location.BOUNDARY) { loc1$1 = Location.INTERIOR; }
111444             switch (overlayOpCode) {
111445               case OverlayOp.INTERSECTION:
111446                 return loc0$1 === Location.INTERIOR && loc1$1 === Location.INTERIOR
111447               case OverlayOp.UNION:
111448                 return loc0$1 === Location.INTERIOR || loc1$1 === Location.INTERIOR
111449               case OverlayOp.DIFFERENCE:
111450                 return loc0$1 === Location.INTERIOR && loc1$1 !== Location.INTERIOR
111451               case OverlayOp.SYMDIFFERENCE:
111452                 return (loc0$1 === Location.INTERIOR && loc1$1 !== Location.INTERIOR) || (loc0$1 !== Location.INTERIOR && loc1$1 === Location.INTERIOR)
111453             }
111454             return false
111455           }
111456         };
111457         OverlayOp.INTERSECTION = 1;
111458         OverlayOp.UNION = 2;
111459         OverlayOp.DIFFERENCE = 3;
111460         OverlayOp.SYMDIFFERENCE = 4;
111461
111462         var FuzzyPointLocator = function FuzzyPointLocator () {
111463           this._g = null;
111464           this._boundaryDistanceTolerance = null;
111465           this._linework = null;
111466           this._ptLocator = new PointLocator();
111467           this._seg = new LineSegment();
111468           var g = arguments[0];
111469           var boundaryDistanceTolerance = arguments[1];
111470           this._g = g;
111471           this._boundaryDistanceTolerance = boundaryDistanceTolerance;
111472           this._linework = this.extractLinework(g);
111473         };
111474         FuzzyPointLocator.prototype.isWithinToleranceOfBoundary = function isWithinToleranceOfBoundary (pt) {
111475             var this$1 = this;
111476
111477           for (var i = 0; i < this._linework.getNumGeometries(); i++) {
111478             var line = this$1._linework.getGeometryN(i);
111479             var seq = line.getCoordinateSequence();
111480             for (var j = 0; j < seq.size() - 1; j++) {
111481               seq.getCoordinate(j, this$1._seg.p0);
111482               seq.getCoordinate(j + 1, this$1._seg.p1);
111483               var dist = this$1._seg.distance(pt);
111484               if (dist <= this$1._boundaryDistanceTolerance) { return true }
111485             }
111486           }
111487           return false
111488         };
111489         FuzzyPointLocator.prototype.getLocation = function getLocation (pt) {
111490           if (this.isWithinToleranceOfBoundary(pt)) { return Location.BOUNDARY }
111491           return this._ptLocator.locate(pt, this._g)
111492         };
111493         FuzzyPointLocator.prototype.extractLinework = function extractLinework (g) {
111494           var extracter = new PolygonalLineworkExtracter();
111495           g.apply(extracter);
111496           var linework = extracter.getLinework();
111497           var lines = GeometryFactory.toLineStringArray(linework);
111498           return g.getFactory().createMultiLineString(lines)
111499         };
111500         FuzzyPointLocator.prototype.interfaces_ = function interfaces_ () {
111501           return []
111502         };
111503         FuzzyPointLocator.prototype.getClass = function getClass () {
111504           return FuzzyPointLocator
111505         };
111506
111507         var PolygonalLineworkExtracter = function PolygonalLineworkExtracter () {
111508           this._linework = null;
111509           this._linework = new ArrayList();
111510         };
111511         PolygonalLineworkExtracter.prototype.getLinework = function getLinework () {
111512           return this._linework
111513         };
111514         PolygonalLineworkExtracter.prototype.filter = function filter (g) {
111515             var this$1 = this;
111516
111517           if (g instanceof Polygon) {
111518             var poly = g;
111519             this._linework.add(poly.getExteriorRing());
111520             for (var i = 0; i < poly.getNumInteriorRing(); i++) {
111521               this$1._linework.add(poly.getInteriorRingN(i));
111522             }
111523           }
111524         };
111525         PolygonalLineworkExtracter.prototype.interfaces_ = function interfaces_ () {
111526           return [GeometryFilter]
111527         };
111528         PolygonalLineworkExtracter.prototype.getClass = function getClass () {
111529           return PolygonalLineworkExtracter
111530         };
111531
111532         var OffsetPointGenerator = function OffsetPointGenerator () {
111533           this._g = null;
111534           this._doLeft = true;
111535           this._doRight = true;
111536           var g = arguments[0];
111537           this._g = g;
111538         };
111539         OffsetPointGenerator.prototype.extractPoints = function extractPoints (line, offsetDistance, offsetPts) {
111540             var this$1 = this;
111541
111542           var pts = line.getCoordinates();
111543           for (var i = 0; i < pts.length - 1; i++) {
111544             this$1.computeOffsetPoints(pts[i], pts[i + 1], offsetDistance, offsetPts);
111545           }
111546         };
111547         OffsetPointGenerator.prototype.setSidesToGenerate = function setSidesToGenerate (doLeft, doRight) {
111548           this._doLeft = doLeft;
111549           this._doRight = doRight;
111550         };
111551         OffsetPointGenerator.prototype.getPoints = function getPoints (offsetDistance) {
111552             var this$1 = this;
111553
111554           var offsetPts = new ArrayList();
111555           var lines = LinearComponentExtracter.getLines(this._g);
111556           for (var i = lines.iterator(); i.hasNext();) {
111557             var line = i.next();
111558             this$1.extractPoints(line, offsetDistance, offsetPts);
111559           }
111560           return offsetPts
111561         };
111562         OffsetPointGenerator.prototype.computeOffsetPoints = function computeOffsetPoints (p0, p1, offsetDistance, offsetPts) {
111563           var dx = p1.x - p0.x;
111564           var dy = p1.y - p0.y;
111565           var len = Math.sqrt(dx * dx + dy * dy);
111566           var ux = offsetDistance * dx / len;
111567           var uy = offsetDistance * dy / len;
111568           var midX = (p1.x + p0.x) / 2;
111569           var midY = (p1.y + p0.y) / 2;
111570           if (this._doLeft) {
111571             var offsetLeft = new Coordinate(midX - uy, midY + ux);
111572             offsetPts.add(offsetLeft);
111573           }
111574           if (this._doRight) {
111575             var offsetRight = new Coordinate(midX + uy, midY - ux);
111576             offsetPts.add(offsetRight);
111577           }
111578         };
111579         OffsetPointGenerator.prototype.interfaces_ = function interfaces_ () {
111580           return []
111581         };
111582         OffsetPointGenerator.prototype.getClass = function getClass () {
111583           return OffsetPointGenerator
111584         };
111585
111586         var OverlayResultValidator = function OverlayResultValidator () {
111587           this._geom = null;
111588           this._locFinder = null;
111589           this._location = new Array(3).fill(null);
111590           this._invalidLocation = null;
111591           this._boundaryDistanceTolerance = OverlayResultValidator.TOLERANCE;
111592           this._testCoords = new ArrayList();
111593           var a = arguments[0];
111594           var b = arguments[1];
111595           var result = arguments[2];
111596           this._boundaryDistanceTolerance = OverlayResultValidator.computeBoundaryDistanceTolerance(a, b);
111597           this._geom = [a, b, result];
111598           this._locFinder = [new FuzzyPointLocator(this._geom[0], this._boundaryDistanceTolerance), new FuzzyPointLocator(this._geom[1], this._boundaryDistanceTolerance), new FuzzyPointLocator(this._geom[2], this._boundaryDistanceTolerance)];
111599         };
111600
111601         var staticAccessors$46 = { TOLERANCE: { configurable: true } };
111602         OverlayResultValidator.prototype.reportResult = function reportResult (overlayOp, location, expectedInterior) {
111603           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]));
111604         };
111605         OverlayResultValidator.prototype.isValid = function isValid (overlayOp) {
111606           this.addTestPts(this._geom[0]);
111607           this.addTestPts(this._geom[1]);
111608           var isValid = this.checkValid(overlayOp);
111609           return isValid
111610         };
111611         OverlayResultValidator.prototype.checkValid = function checkValid () {
111612             var this$1 = this;
111613
111614           if (arguments.length === 1) {
111615             var overlayOp = arguments[0];
111616             for (var i = 0; i < this._testCoords.size(); i++) {
111617               var pt = this$1._testCoords.get(i);
111618               if (!this$1.checkValid(overlayOp, pt)) {
111619                 this$1._invalidLocation = pt;
111620                 return false
111621               }
111622             }
111623             return true
111624           } else if (arguments.length === 2) {
111625             var overlayOp$1 = arguments[0];
111626             var pt$1 = arguments[1];
111627             this._location[0] = this._locFinder[0].getLocation(pt$1);
111628             this._location[1] = this._locFinder[1].getLocation(pt$1);
111629             this._location[2] = this._locFinder[2].getLocation(pt$1);
111630             if (OverlayResultValidator.hasLocation(this._location, Location.BOUNDARY)) { return true }
111631             return this.isValidResult(overlayOp$1, this._location)
111632           }
111633         };
111634         OverlayResultValidator.prototype.addTestPts = function addTestPts (g) {
111635           var ptGen = new OffsetPointGenerator(g);
111636           this._testCoords.addAll(ptGen.getPoints(5 * this._boundaryDistanceTolerance));
111637         };
111638         OverlayResultValidator.prototype.isValidResult = function isValidResult (overlayOp, location) {
111639           var expectedInterior = OverlayOp.isResultOfOp(location[0], location[1], overlayOp);
111640           var resultInInterior = location[2] === Location.INTERIOR;
111641           var isValid = !(expectedInterior ^ resultInInterior);
111642           if (!isValid) { this.reportResult(overlayOp, location, expectedInterior); }
111643           return isValid
111644         };
111645         OverlayResultValidator.prototype.getInvalidLocation = function getInvalidLocation () {
111646           return this._invalidLocation
111647         };
111648         OverlayResultValidator.prototype.interfaces_ = function interfaces_ () {
111649           return []
111650         };
111651         OverlayResultValidator.prototype.getClass = function getClass () {
111652           return OverlayResultValidator
111653         };
111654         OverlayResultValidator.hasLocation = function hasLocation (location, loc) {
111655           for (var i = 0; i < 3; i++) {
111656             if (location[i] === loc) { return true }
111657           }
111658           return false
111659         };
111660         OverlayResultValidator.computeBoundaryDistanceTolerance = function computeBoundaryDistanceTolerance (g0, g1) {
111661           return Math.min(GeometrySnapper.computeSizeBasedSnapTolerance(g0), GeometrySnapper.computeSizeBasedSnapTolerance(g1))
111662         };
111663         OverlayResultValidator.isValid = function isValid (a, b, overlayOp, result) {
111664           var validator = new OverlayResultValidator(a, b, result);
111665           return validator.isValid(overlayOp)
111666         };
111667         staticAccessors$46.TOLERANCE.get = function () { return 0.000001 };
111668
111669         Object.defineProperties( OverlayResultValidator, staticAccessors$46 );
111670
111671         // operation.overlay
111672
111673         var GeometryCombiner = function GeometryCombiner (geoms) {
111674           this._geomFactory = null;
111675           this._skipEmpty = false;
111676           this._inputGeoms = null;
111677           this._geomFactory = GeometryCombiner.extractFactory(geoms);
111678           this._inputGeoms = geoms;
111679         };
111680         GeometryCombiner.prototype.extractElements = function extractElements (geom, elems) {
111681             var this$1 = this;
111682
111683           if (geom === null) { return null }
111684           for (var i = 0; i < geom.getNumGeometries(); i++) {
111685             var elemGeom = geom.getGeometryN(i);
111686             if (this$1._skipEmpty && elemGeom.isEmpty()) { continue }
111687             elems.add(elemGeom);
111688           }
111689         };
111690         GeometryCombiner.prototype.combine = function combine () {
111691             var this$1 = this;
111692
111693           var elems = new ArrayList();
111694           for (var i = this._inputGeoms.iterator(); i.hasNext();) {
111695             var g = i.next();
111696             this$1.extractElements(g, elems);
111697           }
111698           if (elems.size() === 0) {
111699             if (this._geomFactory !== null) {
111700               return this._geomFactory.createGeometryCollection(null)
111701             }
111702             return null
111703           }
111704           return this._geomFactory.buildGeometry(elems)
111705         };
111706         GeometryCombiner.prototype.interfaces_ = function interfaces_ () {
111707           return []
111708         };
111709         GeometryCombiner.prototype.getClass = function getClass () {
111710           return GeometryCombiner
111711         };
111712         GeometryCombiner.combine = function combine () {
111713           if (arguments.length === 1) {
111714             var geoms = arguments[0];
111715             var combiner = new GeometryCombiner(geoms);
111716             return combiner.combine()
111717           } else if (arguments.length === 2) {
111718             var g0 = arguments[0];
111719             var g1 = arguments[1];
111720             var combiner$1 = new GeometryCombiner(GeometryCombiner.createList(g0, g1));
111721             return combiner$1.combine()
111722           } else if (arguments.length === 3) {
111723             var g0$1 = arguments[0];
111724             var g1$1 = arguments[1];
111725             var g2 = arguments[2];
111726             var combiner$2 = new GeometryCombiner(GeometryCombiner.createList(g0$1, g1$1, g2));
111727             return combiner$2.combine()
111728           }
111729         };
111730         GeometryCombiner.extractFactory = function extractFactory (geoms) {
111731           if (geoms.isEmpty()) { return null }
111732           return geoms.iterator().next().getFactory()
111733         };
111734         GeometryCombiner.createList = function createList () {
111735           if (arguments.length === 2) {
111736             var obj0 = arguments[0];
111737             var obj1 = arguments[1];
111738             var list = new ArrayList();
111739             list.add(obj0);
111740             list.add(obj1);
111741             return list
111742           } else if (arguments.length === 3) {
111743             var obj0$1 = arguments[0];
111744             var obj1$1 = arguments[1];
111745             var obj2 = arguments[2];
111746             var list$1 = new ArrayList();
111747             list$1.add(obj0$1);
111748             list$1.add(obj1$1);
111749             list$1.add(obj2);
111750             return list$1
111751           }
111752         };
111753
111754         var CascadedPolygonUnion = function CascadedPolygonUnion () {
111755           this._inputPolys = null;
111756           this._geomFactory = null;
111757           var polys = arguments[0];
111758           this._inputPolys = polys;
111759           if (this._inputPolys === null) { this._inputPolys = new ArrayList(); }
111760         };
111761
111762         var staticAccessors$47 = { STRTREE_NODE_CAPACITY: { configurable: true } };
111763         CascadedPolygonUnion.prototype.reduceToGeometries = function reduceToGeometries (geomTree) {
111764             var this$1 = this;
111765
111766           var geoms = new ArrayList();
111767           for (var i = geomTree.iterator(); i.hasNext();) {
111768             var o = i.next();
111769             var geom = null;
111770             if (hasInterface(o, List)) {
111771               geom = this$1.unionTree(o);
111772             } else if (o instanceof Geometry) {
111773               geom = o;
111774             }
111775             geoms.add(geom);
111776           }
111777           return geoms
111778         };
111779         CascadedPolygonUnion.prototype.extractByEnvelope = function extractByEnvelope (env, geom, disjointGeoms) {
111780           var intersectingGeoms = new ArrayList();
111781           for (var i = 0; i < geom.getNumGeometries(); i++) {
111782             var elem = geom.getGeometryN(i);
111783             if (elem.getEnvelopeInternal().intersects(env)) { intersectingGeoms.add(elem); } else { disjointGeoms.add(elem); }
111784           }
111785           return this._geomFactory.buildGeometry(intersectingGeoms)
111786         };
111787         CascadedPolygonUnion.prototype.unionOptimized = function unionOptimized (g0, g1) {
111788           var g0Env = g0.getEnvelopeInternal();
111789           var g1Env = g1.getEnvelopeInternal();
111790           if (!g0Env.intersects(g1Env)) {
111791             var combo = GeometryCombiner.combine(g0, g1);
111792             return combo
111793           }
111794           if (g0.getNumGeometries() <= 1 && g1.getNumGeometries() <= 1) { return this.unionActual(g0, g1) }
111795           var commonEnv = g0Env.intersection(g1Env);
111796           return this.unionUsingEnvelopeIntersection(g0, g1, commonEnv)
111797         };
111798         CascadedPolygonUnion.prototype.union = function union () {
111799           if (this._inputPolys === null) { throw new Error('union() method cannot be called twice') }
111800           if (this._inputPolys.isEmpty()) { return null }
111801           this._geomFactory = this._inputPolys.iterator().next().getFactory();
111802           var index = new STRtree(CascadedPolygonUnion.STRTREE_NODE_CAPACITY);
111803           for (var i = this._inputPolys.iterator(); i.hasNext();) {
111804             var item = i.next();
111805             index.insert(item.getEnvelopeInternal(), item);
111806           }
111807           this._inputPolys = null;
111808           var itemTree = index.itemsTree();
111809           var unionAll = this.unionTree(itemTree);
111810           return unionAll
111811         };
111812         CascadedPolygonUnion.prototype.binaryUnion = function binaryUnion () {
111813           if (arguments.length === 1) {
111814             var geoms = arguments[0];
111815             return this.binaryUnion(geoms, 0, geoms.size())
111816           } else if (arguments.length === 3) {
111817             var geoms$1 = arguments[0];
111818             var start = arguments[1];
111819             var end = arguments[2];
111820             if (end - start <= 1) {
111821               var g0 = CascadedPolygonUnion.getGeometry(geoms$1, start);
111822               return this.unionSafe(g0, null)
111823             } else if (end - start === 2) {
111824               return this.unionSafe(CascadedPolygonUnion.getGeometry(geoms$1, start), CascadedPolygonUnion.getGeometry(geoms$1, start + 1))
111825             } else {
111826               var mid = Math.trunc((end + start) / 2);
111827               var g0$1 = this.binaryUnion(geoms$1, start, mid);
111828               var g1 = this.binaryUnion(geoms$1, mid, end);
111829               return this.unionSafe(g0$1, g1)
111830             }
111831           }
111832         };
111833         CascadedPolygonUnion.prototype.repeatedUnion = function repeatedUnion (geoms) {
111834           var union = null;
111835           for (var i = geoms.iterator(); i.hasNext();) {
111836             var g = i.next();
111837             if (union === null) { union = g.copy(); } else { union = union.union(g); }
111838           }
111839           return union
111840         };
111841         CascadedPolygonUnion.prototype.unionSafe = function unionSafe (g0, g1) {
111842           if (g0 === null && g1 === null) { return null }
111843           if (g0 === null) { return g1.copy() }
111844           if (g1 === null) { return g0.copy() }
111845           return this.unionOptimized(g0, g1)
111846         };
111847         CascadedPolygonUnion.prototype.unionActual = function unionActual (g0, g1) {
111848           return CascadedPolygonUnion.restrictToPolygons(g0.union(g1))
111849         };
111850         CascadedPolygonUnion.prototype.unionTree = function unionTree (geomTree) {
111851           var geoms = this.reduceToGeometries(geomTree);
111852           var union = this.binaryUnion(geoms);
111853           return union
111854         };
111855         CascadedPolygonUnion.prototype.unionUsingEnvelopeIntersection = function unionUsingEnvelopeIntersection (g0, g1, common) {
111856           var disjointPolys = new ArrayList();
111857           var g0Int = this.extractByEnvelope(common, g0, disjointPolys);
111858           var g1Int = this.extractByEnvelope(common, g1, disjointPolys);
111859           var union = this.unionActual(g0Int, g1Int);
111860           disjointPolys.add(union);
111861           var overallUnion = GeometryCombiner.combine(disjointPolys);
111862           return overallUnion
111863         };
111864         CascadedPolygonUnion.prototype.bufferUnion = function bufferUnion () {
111865           if (arguments.length === 1) {
111866             var geoms = arguments[0];
111867             var factory = geoms.get(0).getFactory();
111868             var gColl = factory.buildGeometry(geoms);
111869             var unionAll = gColl.buffer(0.0);
111870             return unionAll
111871           } else if (arguments.length === 2) {
111872             var g0 = arguments[0];
111873             var g1 = arguments[1];
111874             var factory$1 = g0.getFactory();
111875             var gColl$1 = factory$1.createGeometryCollection([g0, g1]);
111876             var unionAll$1 = gColl$1.buffer(0.0);
111877             return unionAll$1
111878           }
111879         };
111880         CascadedPolygonUnion.prototype.interfaces_ = function interfaces_ () {
111881           return []
111882         };
111883         CascadedPolygonUnion.prototype.getClass = function getClass () {
111884           return CascadedPolygonUnion
111885         };
111886         CascadedPolygonUnion.restrictToPolygons = function restrictToPolygons (g) {
111887           if (hasInterface(g, Polygonal)) {
111888             return g
111889           }
111890           var polygons = PolygonExtracter.getPolygons(g);
111891           if (polygons.size() === 1) { return polygons.get(0) }
111892           return g.getFactory().createMultiPolygon(GeometryFactory.toPolygonArray(polygons))
111893         };
111894         CascadedPolygonUnion.getGeometry = function getGeometry (list, index) {
111895           if (index >= list.size()) { return null }
111896           return list.get(index)
111897         };
111898         CascadedPolygonUnion.union = function union (polys) {
111899           var op = new CascadedPolygonUnion(polys);
111900           return op.union()
111901         };
111902         staticAccessors$47.STRTREE_NODE_CAPACITY.get = function () { return 4 };
111903
111904         Object.defineProperties( CascadedPolygonUnion, staticAccessors$47 );
111905
111906         var UnionOp = function UnionOp () {};
111907
111908         UnionOp.prototype.interfaces_ = function interfaces_ () {
111909           return []
111910         };
111911         UnionOp.prototype.getClass = function getClass () {
111912           return UnionOp
111913         };
111914         UnionOp.union = function union (g, other) {
111915           if (g.isEmpty() || other.isEmpty()) {
111916             if (g.isEmpty() && other.isEmpty()) { return OverlayOp.createEmptyResult(OverlayOp.UNION, g, other, g.getFactory()) }
111917             if (g.isEmpty()) { return other.copy() }
111918             if (other.isEmpty()) { return g.copy() }
111919           }
111920           g.checkNotGeometryCollection(g);
111921           g.checkNotGeometryCollection(other);
111922           return SnapIfNeededOverlayOp.overlayOp(g, other, OverlayOp.UNION)
111923         };
111924
111925         /**
111926          * Earth Radius used with the Harvesine formula and approximates using a spherical (non-ellipsoid) Earth.
111927          */
111928
111929         /**
111930          * Wraps a GeoJSON {@link Geometry} in a GeoJSON {@link Feature}.
111931          *
111932          * @name feature
111933          * @param {Geometry} geometry input geometry
111934          * @param {Object} [properties={}] an Object of key-value pairs to add as properties
111935          * @param {Object} [options={}] Optional Parameters
111936          * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
111937          * @param {string|number} [options.id] Identifier associated with the Feature
111938          * @returns {Feature} a GeoJSON Feature
111939          * @example
111940          * var geometry = {
111941          *   "type": "Point",
111942          *   "coordinates": [110, 50]
111943          * };
111944          *
111945          * var feature = turf.feature(geometry);
111946          *
111947          * //=feature
111948          */
111949         function feature$1(geometry, properties, options) {
111950             // Optional Parameters
111951             options = options || {};
111952             if (!isObject$4(options)) { throw new Error('options is invalid'); }
111953             var bbox = options.bbox;
111954             var id = options.id;
111955
111956             // Validation
111957             if (geometry === undefined) { throw new Error('geometry is required'); }
111958             if (properties && properties.constructor !== Object) { throw new Error('properties must be an Object'); }
111959             if (bbox) { validateBBox(bbox); }
111960             if (id) { validateId(id); }
111961
111962             // Main
111963             var feat = {type: 'Feature'};
111964             if (id) { feat.id = id; }
111965             if (bbox) { feat.bbox = bbox; }
111966             feat.properties = properties || {};
111967             feat.geometry = geometry;
111968             return feat;
111969         }
111970
111971         /**
111972          * isNumber
111973          *
111974          * @param {*} num Number to validate
111975          * @returns {boolean} true/false
111976          * @example
111977          * turf.isNumber(123)
111978          * //=true
111979          * turf.isNumber('foo')
111980          * //=false
111981          */
111982         function isNumber$1(num) {
111983             return !isNaN(num) && num !== null && !Array.isArray(num);
111984         }
111985
111986         /**
111987          * isObject
111988          *
111989          * @param {*} input variable to validate
111990          * @returns {boolean} true/false
111991          * @example
111992          * turf.isObject({elevation: 10})
111993          * //=true
111994          * turf.isObject('foo')
111995          * //=false
111996          */
111997         function isObject$4(input) {
111998             return (!!input) && (input.constructor === Object);
111999         }
112000
112001         /**
112002          * Validate BBox
112003          *
112004          * @private
112005          * @param {Array<number>} bbox BBox to validate
112006          * @returns {void}
112007          * @throws Error if BBox is not valid
112008          * @example
112009          * validateBBox([-180, -40, 110, 50])
112010          * //=OK
112011          * validateBBox([-180, -40])
112012          * //=Error
112013          * validateBBox('Foo')
112014          * //=Error
112015          * validateBBox(5)
112016          * //=Error
112017          * validateBBox(null)
112018          * //=Error
112019          * validateBBox(undefined)
112020          * //=Error
112021          */
112022         function validateBBox(bbox) {
112023             if (!bbox) { throw new Error('bbox is required'); }
112024             if (!Array.isArray(bbox)) { throw new Error('bbox must be an Array'); }
112025             if (bbox.length !== 4 && bbox.length !== 6) { throw new Error('bbox must be an Array of 4 or 6 numbers'); }
112026             bbox.forEach(function (num) {
112027                 if (!isNumber$1(num)) { throw new Error('bbox must only contain numbers'); }
112028             });
112029         }
112030
112031         /**
112032          * Validate Id
112033          *
112034          * @private
112035          * @param {string|number} id Id to validate
112036          * @returns {void}
112037          * @throws Error if Id is not valid
112038          * @example
112039          * validateId([-180, -40, 110, 50])
112040          * //=Error
112041          * validateId([-180, -40])
112042          * //=Error
112043          * validateId('Foo')
112044          * //=OK
112045          * validateId(5)
112046          * //=OK
112047          * validateId(null)
112048          * //=Error
112049          * validateId(undefined)
112050          * //=Error
112051          */
112052         function validateId(id) {
112053             if (!id) { throw new Error('id is required'); }
112054             if (['string', 'number'].indexOf(typeof id) === -1) { throw new Error('id must be a number or a string'); }
112055         }
112056
112057         /**
112058          * Callback for geomEach
112059          *
112060          * @callback geomEachCallback
112061          * @param {Geometry} currentGeometry The current Geometry being processed.
112062          * @param {number} featureIndex The current index of the Feature being processed.
112063          * @param {Object} featureProperties The current Feature Properties being processed.
112064          * @param {Array<number>} featureBBox The current Feature BBox being processed.
112065          * @param {number|string} featureId The current Feature Id being processed.
112066          */
112067
112068         /**
112069          * Iterate over each geometry in any GeoJSON object, similar to Array.forEach()
112070          *
112071          * @name geomEach
112072          * @param {FeatureCollection|Feature|Geometry} geojson any GeoJSON object
112073          * @param {Function} callback a method that takes (currentGeometry, featureIndex, featureProperties, featureBBox, featureId)
112074          * @returns {void}
112075          * @example
112076          * var features = turf.featureCollection([
112077          *     turf.point([26, 37], {foo: 'bar'}),
112078          *     turf.point([36, 53], {hello: 'world'})
112079          * ]);
112080          *
112081          * turf.geomEach(features, function (currentGeometry, featureIndex, featureProperties, featureBBox, featureId) {
112082          *   //=currentGeometry
112083          *   //=featureIndex
112084          *   //=featureProperties
112085          *   //=featureBBox
112086          *   //=featureId
112087          * });
112088          */
112089         function geomEach(geojson, callback) {
112090             var i, j, g, geometry, stopG,
112091                 geometryMaybeCollection,
112092                 isGeometryCollection,
112093                 featureProperties,
112094                 featureBBox,
112095                 featureId,
112096                 featureIndex = 0,
112097                 isFeatureCollection = geojson.type === 'FeatureCollection',
112098                 isFeature = geojson.type === 'Feature',
112099                 stop = isFeatureCollection ? geojson.features.length : 1;
112100
112101             // This logic may look a little weird. The reason why it is that way
112102             // is because it's trying to be fast. GeoJSON supports multiple kinds
112103             // of objects at its root: FeatureCollection, Features, Geometries.
112104             // This function has the responsibility of handling all of them, and that
112105             // means that some of the `for` loops you see below actually just don't apply
112106             // to certain inputs. For instance, if you give this just a
112107             // Point geometry, then both loops are short-circuited and all we do
112108             // is gradually rename the input until it's called 'geometry'.
112109             //
112110             // This also aims to allocate as few resources as possible: just a
112111             // few numbers and booleans, rather than any temporary arrays as would
112112             // be required with the normalization approach.
112113             for (i = 0; i < stop; i++) {
112114
112115                 geometryMaybeCollection = (isFeatureCollection ? geojson.features[i].geometry :
112116                     (isFeature ? geojson.geometry : geojson));
112117                 featureProperties = (isFeatureCollection ? geojson.features[i].properties :
112118                     (isFeature ? geojson.properties : {}));
112119                 featureBBox = (isFeatureCollection ? geojson.features[i].bbox :
112120                     (isFeature ? geojson.bbox : undefined));
112121                 featureId = (isFeatureCollection ? geojson.features[i].id :
112122                     (isFeature ? geojson.id : undefined));
112123                 isGeometryCollection = (geometryMaybeCollection) ? geometryMaybeCollection.type === 'GeometryCollection' : false;
112124                 stopG = isGeometryCollection ? geometryMaybeCollection.geometries.length : 1;
112125
112126                 for (g = 0; g < stopG; g++) {
112127                     geometry = isGeometryCollection ?
112128                         geometryMaybeCollection.geometries[g] : geometryMaybeCollection;
112129
112130                     // Handle null Geometry
112131                     if (geometry === null) {
112132                         if (callback(null, featureIndex, featureProperties, featureBBox, featureId) === false) { return false; }
112133                         continue;
112134                     }
112135                     switch (geometry.type) {
112136                     case 'Point':
112137                     case 'LineString':
112138                     case 'MultiPoint':
112139                     case 'Polygon':
112140                     case 'MultiLineString':
112141                     case 'MultiPolygon': {
112142                         if (callback(geometry, featureIndex, featureProperties, featureBBox, featureId) === false) { return false; }
112143                         break;
112144                     }
112145                     case 'GeometryCollection': {
112146                         for (j = 0; j < geometry.geometries.length; j++) {
112147                             if (callback(geometry.geometries[j], featureIndex, featureProperties, featureBBox, featureId) === false) { return false; }
112148                         }
112149                         break;
112150                     }
112151                     default:
112152                         throw new Error('Unknown Geometry Type');
112153                     }
112154                 }
112155                 // Only increase `featureIndex` per each feature
112156                 featureIndex++;
112157             }
112158         }
112159
112160         /**
112161          * Callback for geomReduce
112162          *
112163          * The first time the callback function is called, the values provided as arguments depend
112164          * on whether the reduce method has an initialValue argument.
112165          *
112166          * If an initialValue is provided to the reduce method:
112167          *  - The previousValue argument is initialValue.
112168          *  - The currentValue argument is the value of the first element present in the array.
112169          *
112170          * If an initialValue is not provided:
112171          *  - The previousValue argument is the value of the first element present in the array.
112172          *  - The currentValue argument is the value of the second element present in the array.
112173          *
112174          * @callback geomReduceCallback
112175          * @param {*} previousValue The accumulated value previously returned in the last invocation
112176          * of the callback, or initialValue, if supplied.
112177          * @param {Geometry} currentGeometry The current Geometry being processed.
112178          * @param {number} featureIndex The current index of the Feature being processed.
112179          * @param {Object} featureProperties The current Feature Properties being processed.
112180          * @param {Array<number>} featureBBox The current Feature BBox being processed.
112181          * @param {number|string} featureId The current Feature Id being processed.
112182          */
112183
112184         /**
112185          * Reduce geometry in any GeoJSON object, similar to Array.reduce().
112186          *
112187          * @name geomReduce
112188          * @param {FeatureCollection|Feature|Geometry} geojson any GeoJSON object
112189          * @param {Function} callback a method that takes (previousValue, currentGeometry, featureIndex, featureProperties, featureBBox, featureId)
112190          * @param {*} [initialValue] Value to use as the first argument to the first call of the callback.
112191          * @returns {*} The value that results from the reduction.
112192          * @example
112193          * var features = turf.featureCollection([
112194          *     turf.point([26, 37], {foo: 'bar'}),
112195          *     turf.point([36, 53], {hello: 'world'})
112196          * ]);
112197          *
112198          * turf.geomReduce(features, function (previousValue, currentGeometry, featureIndex, featureProperties, featureBBox, featureId) {
112199          *   //=previousValue
112200          *   //=currentGeometry
112201          *   //=featureIndex
112202          *   //=featureProperties
112203          *   //=featureBBox
112204          *   //=featureId
112205          *   return currentGeometry
112206          * });
112207          */
112208         function geomReduce(geojson, callback, initialValue) {
112209             var previousValue = initialValue;
112210             geomEach(geojson, function (currentGeometry, featureIndex, featureProperties, featureBBox, featureId) {
112211                 if (featureIndex === 0 && initialValue === undefined) { previousValue = currentGeometry; }
112212                 else { previousValue = callback(previousValue, currentGeometry, featureIndex, featureProperties, featureBBox, featureId); }
112213             });
112214             return previousValue;
112215         }
112216
112217         /**
112218          * Callback for flattenEach
112219          *
112220          * @callback flattenEachCallback
112221          * @param {Feature} currentFeature The current flattened feature being processed.
112222          * @param {number} featureIndex The current index of the Feature being processed.
112223          * @param {number} multiFeatureIndex The current index of the Multi-Feature being processed.
112224          */
112225
112226         /**
112227          * Iterate over flattened features in any GeoJSON object, similar to
112228          * Array.forEach.
112229          *
112230          * @name flattenEach
112231          * @param {FeatureCollection|Feature|Geometry} geojson any GeoJSON object
112232          * @param {Function} callback a method that takes (currentFeature, featureIndex, multiFeatureIndex)
112233          * @example
112234          * var features = turf.featureCollection([
112235          *     turf.point([26, 37], {foo: 'bar'}),
112236          *     turf.multiPoint([[40, 30], [36, 53]], {hello: 'world'})
112237          * ]);
112238          *
112239          * turf.flattenEach(features, function (currentFeature, featureIndex, multiFeatureIndex) {
112240          *   //=currentFeature
112241          *   //=featureIndex
112242          *   //=multiFeatureIndex
112243          * });
112244          */
112245         function flattenEach(geojson, callback) {
112246             geomEach(geojson, function (geometry, featureIndex, properties, bbox, id) {
112247                 // Callback for single geometry
112248                 var type = (geometry === null) ? null : geometry.type;
112249                 switch (type) {
112250                 case null:
112251                 case 'Point':
112252                 case 'LineString':
112253                 case 'Polygon':
112254                     if (callback(feature$1(geometry, properties, {bbox: bbox, id: id}), featureIndex, 0) === false) { return false; }
112255                     return;
112256                 }
112257
112258                 var geomType;
112259
112260                 // Callback for multi-geometry
112261                 switch (type) {
112262                 case 'MultiPoint':
112263                     geomType = 'Point';
112264                     break;
112265                 case 'MultiLineString':
112266                     geomType = 'LineString';
112267                     break;
112268                 case 'MultiPolygon':
112269                     geomType = 'Polygon';
112270                     break;
112271                 }
112272
112273                 for (var multiFeatureIndex = 0; multiFeatureIndex < geometry.coordinates.length; multiFeatureIndex++) {
112274                     var coordinate = geometry.coordinates[multiFeatureIndex];
112275                     var geom = {
112276                         type: geomType,
112277                         coordinates: coordinate
112278                     };
112279                     if (callback(feature$1(geom, properties), featureIndex, multiFeatureIndex) === false) { return false; }
112280                 }
112281             });
112282         }
112283
112284         /**
112285          * Takes one or more features and returns their area in square meters.
112286          *
112287          * @name area
112288          * @param {GeoJSON} geojson input GeoJSON feature(s)
112289          * @returns {number} area in square meters
112290          * @example
112291          * var polygon = turf.polygon([[[125, -15], [113, -22], [154, -27], [144, -15], [125, -15]]]);
112292          *
112293          * var area = turf.area(polygon);
112294          *
112295          * //addToMap
112296          * var addToMap = [polygon]
112297          * polygon.properties.area = area
112298          */
112299         function area(geojson) {
112300             return geomReduce(geojson, function (value, geom) {
112301                 return value + calculateArea(geom);
112302             }, 0);
112303         }
112304
112305         var RADIUS$1 = 6378137;
112306         // var FLATTENING_DENOM = 298.257223563;
112307         // var FLATTENING = 1 / FLATTENING_DENOM;
112308         // var POLAR_RADIUS = RADIUS * (1 - FLATTENING);
112309
112310         /**
112311          * Calculate Area
112312          *
112313          * @private
112314          * @param {GeoJSON} geojson GeoJSON
112315          * @returns {number} area
112316          */
112317         function calculateArea(geojson) {
112318             var area = 0, i;
112319             switch (geojson.type) {
112320             case 'Polygon':
112321                 return polygonArea$1(geojson.coordinates);
112322             case 'MultiPolygon':
112323                 for (i = 0; i < geojson.coordinates.length; i++) {
112324                     area += polygonArea$1(geojson.coordinates[i]);
112325                 }
112326                 return area;
112327             case 'Point':
112328             case 'MultiPoint':
112329             case 'LineString':
112330             case 'MultiLineString':
112331                 return 0;
112332             case 'GeometryCollection':
112333                 for (i = 0; i < geojson.geometries.length; i++) {
112334                     area += calculateArea(geojson.geometries[i]);
112335                 }
112336                 return area;
112337             }
112338         }
112339
112340         function polygonArea$1(coords) {
112341             var area = 0;
112342             if (coords && coords.length > 0) {
112343                 area += Math.abs(ringArea$1(coords[0]));
112344                 for (var i = 1; i < coords.length; i++) {
112345                     area -= Math.abs(ringArea$1(coords[i]));
112346                 }
112347             }
112348             return area;
112349         }
112350
112351         /**
112352          * @private
112353          * Calculate the approximate area of the polygon were it projected onto the earth.
112354          * Note that this area will be positive if ring is oriented clockwise, otherwise it will be negative.
112355          *
112356          * Reference:
112357          * Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for Polygons on a Sphere", JPL Publication 07-03, Jet Propulsion
112358          * Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409
112359          *
112360          * @param {Array<Array<number>>} coords Ring Coordinates
112361          * @returns {number} The approximate signed geodesic area of the polygon in square meters.
112362          */
112363         function ringArea$1(coords) {
112364             var p1;
112365             var p2;
112366             var p3;
112367             var lowerIndex;
112368             var middleIndex;
112369             var upperIndex;
112370             var i;
112371             var area = 0;
112372             var coordsLength = coords.length;
112373
112374             if (coordsLength > 2) {
112375                 for (i = 0; i < coordsLength; i++) {
112376                     if (i === coordsLength - 2) { // i = N-2
112377                         lowerIndex = coordsLength - 2;
112378                         middleIndex = coordsLength - 1;
112379                         upperIndex = 0;
112380                     } else if (i === coordsLength - 1) { // i = N-1
112381                         lowerIndex = coordsLength - 1;
112382                         middleIndex = 0;
112383                         upperIndex = 1;
112384                     } else { // i = 0 to N-3
112385                         lowerIndex = i;
112386                         middleIndex = i + 1;
112387                         upperIndex = i + 2;
112388                     }
112389                     p1 = coords[lowerIndex];
112390                     p2 = coords[middleIndex];
112391                     p3 = coords[upperIndex];
112392                     area += (rad$1(p3[0]) - rad$1(p1[0])) * Math.sin(rad$1(p2[1]));
112393                 }
112394
112395                 area = area * RADIUS$1 * RADIUS$1 / 2;
112396             }
112397
112398             return area;
112399         }
112400
112401         function rad$1(_) {
112402             return _ * Math.PI / 180;
112403         }
112404
112405         /**
112406          * Get Geometry from Feature or Geometry Object
112407          *
112408          * @param {Feature|Geometry} geojson GeoJSON Feature or Geometry Object
112409          * @returns {Geometry|null} GeoJSON Geometry Object
112410          * @throws {Error} if geojson is not a Feature or Geometry Object
112411          * @example
112412          * var point = {
112413          *   "type": "Feature",
112414          *   "properties": {},
112415          *   "geometry": {
112416          *     "type": "Point",
112417          *     "coordinates": [110, 40]
112418          *   }
112419          * }
112420          * var geom = turf.getGeom(point)
112421          * //={"type": "Point", "coordinates": [110, 40]}
112422          */
112423         function getGeom(geojson) {
112424             if (!geojson) { throw new Error('geojson is required'); }
112425             if (geojson.geometry !== undefined) { return geojson.geometry; }
112426             if (geojson.coordinates || geojson.geometries) { return geojson; }
112427             throw new Error('geojson must be a valid Feature or Geometry Object');
112428         }
112429
112430         /**
112431          * Finds the difference between two {@link Polygon|polygons} by clipping the second polygon from the first.
112432          *
112433          * @name difference
112434          * @param {Feature<Polygon|MultiPolygon>} polygon1 input Polygon feature
112435          * @param {Feature<Polygon|MultiPolygon>} polygon2 Polygon feature to difference from polygon1
112436          * @returns {Feature<Polygon|MultiPolygon>|null} a Polygon or MultiPolygon feature showing the area of `polygon1` excluding the area of `polygon2` (if empty returns `null`)
112437          * @example
112438          * var polygon1 = turf.polygon([[
112439          *   [128, -26],
112440          *   [141, -26],
112441          *   [141, -21],
112442          *   [128, -21],
112443          *   [128, -26]
112444          * ]], {
112445          *   "fill": "#F00",
112446          *   "fill-opacity": 0.1
112447          * });
112448          * var polygon2 = turf.polygon([[
112449          *   [126, -28],
112450          *   [140, -28],
112451          *   [140, -20],
112452          *   [126, -20],
112453          *   [126, -28]
112454          * ]], {
112455          *   "fill": "#00F",
112456          *   "fill-opacity": 0.1
112457          * });
112458          *
112459          * var difference = turf.difference(polygon1, polygon2);
112460          *
112461          * //addToMap
112462          * var addToMap = [polygon1, polygon2, difference];
112463          */
112464         function difference(polygon1, polygon2) {
112465             var geom1 = getGeom(polygon1);
112466             var geom2 = getGeom(polygon2);
112467             var properties = polygon1.properties || {};
112468
112469             // Issue #721 - JSTS can't handle empty polygons
112470             geom1 = removeEmptyPolygon(geom1);
112471             geom2 = removeEmptyPolygon(geom2);
112472             if (!geom1) { return null; }
112473             if (!geom2) { return feature$1(geom1, properties); }
112474
112475             // JSTS difference operation
112476             var reader = new GeoJSONReader();
112477             var a = reader.read(geom1);
112478             var b = reader.read(geom2);
112479             var differenced = OverlayOp.difference(a, b);
112480             if (differenced.isEmpty()) { return null; }
112481             var writer = new GeoJSONWriter();
112482             var geom = writer.write(differenced);
112483
112484             return feature$1(geom, properties);
112485         }
112486
112487         /**
112488          * Detect Empty Polygon
112489          *
112490          * @private
112491          * @param {Geometry<Polygon|MultiPolygon>} geom Geometry Object
112492          * @returns {Geometry<Polygon|MultiPolygon>|null} removed any polygons with no areas
112493          */
112494         function removeEmptyPolygon(geom) {
112495             switch (geom.type) {
112496             case 'Polygon':
112497                 if (area(geom) > 1) { return geom; }
112498                 return null;
112499             case 'MultiPolygon':
112500                 var coordinates = [];
112501                 flattenEach(geom, function (feature$$1) {
112502                     if (area(feature$$1) > 1) { coordinates.push(feature$$1.geometry.coordinates); }
112503                 });
112504                 if (coordinates.length) { return {type: 'MultiPolygon', coordinates: coordinates}; }
112505             }
112506         }
112507
112508         /**
112509          * 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.
112510          *
112511          * @name union
112512          * @param {...Feature<Polygon>} A polygon to combine
112513          * @returns {Feature<(Polygon|MultiPolygon)>} a combined {@link Polygon} or {@link MultiPolygon} feature
112514          * @example
112515          * var poly1 = turf.polygon([[
112516          *     [-82.574787, 35.594087],
112517          *     [-82.574787, 35.615581],
112518          *     [-82.545261, 35.615581],
112519          *     [-82.545261, 35.594087],
112520          *     [-82.574787, 35.594087]
112521          * ]], {"fill": "#0f0"});
112522          * var poly2 = turf.polygon([[
112523          *     [-82.560024, 35.585153],
112524          *     [-82.560024, 35.602602],
112525          *     [-82.52964, 35.602602],
112526          *     [-82.52964, 35.585153],
112527          *     [-82.560024, 35.585153]
112528          * ]], {"fill": "#00f"});
112529          *
112530          * var union = turf.union(poly1, poly2);
112531          *
112532          * //addToMap
112533          * var addToMap = [poly1, poly2, union];
112534          */
112535         function union$1() {
112536             var arguments$1 = arguments;
112537
112538             var reader = new GeoJSONReader();
112539             var result = reader.read(JSON.stringify(arguments[0].geometry));
112540
112541             for (var i = 1; i < arguments.length; i++) {
112542                 result = UnionOp.union(result, reader.read(JSON.stringify(arguments$1[i].geometry)));
112543             }
112544
112545             var writer = new GeoJSONWriter();
112546             result = writer.write(result);
112547
112548             return {
112549                 type: 'Feature',
112550                 geometry: result,
112551                 properties: arguments[0].properties
112552             };
112553         }
112554
112555         // Reduce an array of locations into a single GeoJSON feature
112556         function _locationReducer(accumulator, location) {
112557           /* eslint-disable no-console, no-invalid-this */
112558           var result;
112559           try {
112560             var resolved = this.resolveLocation(location);
112561             if (!resolved || !resolved.feature) {
112562               console.warn(("Warning:  Couldn't resolve location \"" + location + "\""));
112563               return accumulator;
112564             }
112565             result = !accumulator ? resolved.feature : union$1(accumulator, resolved.feature);
112566           } catch (e) {
112567             console.warn(("Warning:  Error resolving location \"" + location + "\""));
112568             console.warn(e);
112569             result = accumulator;
112570           }
112571
112572           return result;
112573           /* eslint-enable no-console, no-invalid-this */
112574         }
112575
112576
112577
112578         function _cloneDeep(obj) {
112579           return JSON.parse(JSON.stringify(obj));
112580         }
112581
112582
112583         var defaultExport = function defaultExport(fc) {
112584           var this$1 = this;
112585
112586           this._cache = {};
112587
112588           // process input FeatureCollection
112589           if (fc && fc.type === 'FeatureCollection' && Array.isArray(fc.features)) {
112590             fc.features.forEach(function (feature) {
112591               feature.properties = feature.properties || {};
112592               var props = feature.properties;
112593
112594               // get `id` from either `id` or `properties`
112595               var id = feature.id || props.id;
112596               if (!id || !/^\S+\.geojson$/i.test(id)) { return; }
112597
112598               // ensure id exists and is lowercase
112599               id = id.toLowerCase();
112600               feature.id = id;
112601               props.id = id;
112602
112603               // ensure area property exists
112604               if (!props.area) {
112605                 var area = geojsonArea.geometry(feature.geometry) / 1e6;// m² to km²
112606                 props.area = Number(area.toFixed(2));
112607               }
112608
112609               this$1._cache[id] = feature;
112610             });
112611           }
112612
112613           // Replace CountryCoder world geometry to have a polygon covering the world.
112614           var world = _cloneDeep(feature('Q2'));
112615           world.geometry = {
112616             type: 'Polygon',
112617             coordinates: [[[-180, -90], [180, -90], [180, 90], [-180, 90], [-180, -90]]]
112618           };
112619           world.id = 'Q2';
112620           world.properties.id = 'Q2';
112621           world.properties.area = geojsonArea.geometry(world.geometry) / 1e6;// m² to km²
112622           this._cache.Q2 = world;
112623         };
112624
112625
112626         // validateLocation
112627         //
112628         // Pass a `location` identifier
112629         // Returns a result like
112630         // {
112631         //   type:   'point', 'geojson', or 'countrycoder'
112632         //   location:the queried location
112633         //   id:      a unique identifier
112634         // }
112635         //or `null` if the location is invalid
112636         //
112637         defaultExport.prototype.validateLocation = function validateLocation (location) {
112638           if (Array.isArray(location)) { // a [lon,lat] coordinate pair?
112639             if (location.length === 2 && Number.isFinite(location[0]) && Number.isFinite(location[1]) &&
112640               location[0] >= -180 && location[0] <= 180 && location[1] >= -90 && location[1] <= 90
112641             ) {
112642               var id = '[' + location.toString() + ']';
112643               return { type: 'point', location: location, id: id };
112644             }
112645
112646           } else if (typeof location === 'string' && /^\S+\.geojson$/i.test(location)) { // a .geojson filename?
112647             var id$1 = location.toLowerCase();
112648             if (this._cache[id$1]) {
112649               return { type: 'geojson', location: location, id: id$1 };
112650             }
112651
112652           } else if (typeof location === 'string' || typeof location === 'number') { // a country-coder value?
112653             var feature$1 = feature(location);
112654             if (feature$1) {
112655               // Use wikidata QID as the identifier, since that seems to be the only
112656               // property that everything in CountryCoder is guaranteed to have.
112657               var id$2 = feature$1.properties.wikidata;
112658               return { type: 'countrycoder', location: location, id: id$2 };
112659             }
112660           }
112661
112662           return null;
112663         };
112664
112665
112666         // resolveLocation
112667         //
112668         // Pass a `location` identifier
112669         // Returns a result like
112670         // {
112671         //   type:    'point', 'geojson', or 'countrycoder'
112672         //   location:the queried location
112673         //   id:      a unique identifier
112674         //   feature: the geojson feature
112675         // }
112676         //or `null` if the location is invalid
112677         //
112678         defaultExport.prototype.resolveLocation = function resolveLocation (location) {
112679           var valid = this.validateLocation(location);
112680           if (!valid) { return null; }
112681
112682           // return a result from cache if we can
112683           if (this._cache[valid.id]) {
112684             return Object.assign(valid, { feature: this._cache[valid.id] });
112685           }
112686
112687           // a [lon,lat] coordinate pair?
112688           if (valid.type === 'point') {
112689             var RADIUS = 25000;// meters
112690             var EDGES = 10;
112691             var PRECISION = 3;
112692             var area = Math.PI * RADIUS * RADIUS / 1e6;   // m² to km²
112693             var feature$1 = this._cache[valid.id] = geojsonPrecision({
112694               type: 'Feature',
112695               id: valid.id,
112696               properties: { id: valid.id, area: Number(area.toFixed(2)) },
112697               geometry: circleToPolygon(location, RADIUS, EDGES)
112698             }, PRECISION);
112699             return Object.assign(valid, { feature: feature$1 });
112700
112701           // a .geojson filename?
112702           } else if (valid.type === 'geojson') ; else if (valid.type === 'countrycoder') {
112703             var feature$1$1 = _cloneDeep(feature(valid.id));
112704             var props = feature$1$1.properties;
112705
112706             // -> This block of code is weird and requires some explanation. <-
112707             // CountryCoder includes higher level features which are made up of members.
112708             // These features don't have their own geometry, but CountryCoder provides an
112709             // `aggregateFeature` method to combine these members into a MultiPolygon.
112710             // BUT, when we try to actually work with these aggregated MultiPolygons,
112711             // Turf/JSTS gets crashy because of topography bugs.
112712             // SO, we'll aggregate the features ourselves by unioning them together.
112713             // This approach also has the benefit of removing all the internal boaders and
112714             // simplifying the regional polygons a lot.
112715             if (Array.isArray(props.members)) {
112716               var seed = feature$1$1.geometry ? feature$1$1 : null;
112717               var aggregate = props.members.reduce(_locationReducer.bind(this), seed);
112718               feature$1$1.geometry = aggregate.geometry;
112719             }
112720
112721             // ensure area property exists
112722             if (!props.area) {
112723               var area$1 = geojsonArea.geometry(feature$1$1.geometry) / 1e6;// m² to km²
112724               props.area = Number(area$1.toFixed(2));
112725             }
112726
112727             // ensure id property exists
112728             feature$1$1.id = valid.id;
112729             props.id = valid.id;
112730
112731             this._cache[valid.id] = feature$1$1;
112732             return Object.assign(valid, { feature: feature$1$1 });
112733           }
112734
112735           return null;
112736         };
112737
112738
112739         // resolveLocationSet
112740         //
112741         // Pass a `locationSet` Object like:
112742         // `{ include: [ Array ], exclude: [ Array ] }`
112743         // Returns a stable identifier string of the form:
112744         // "+[included]-[excluded]"
112745         //
112746         defaultExport.prototype.resolveLocationSet = function resolveLocationSet (locationSet) {
112747           locationSet = locationSet || {};
112748           var resolve = this.resolveLocation.bind(this);
112749           var include = (locationSet.include || []).map(resolve).filter(Boolean);
112750           var exclude = (locationSet.exclude || []).map(resolve).filter(Boolean);
112751
112752           if (!include.length) {
112753             include = [resolve('Q2')]; // default to 'the world'
112754           }
112755
112756           // return quickly if it's a single included location..
112757           if (include.length === 1 && exclude.length === 0) {
112758             return include[0].feature;
112759           }
112760
112761           // generate stable identifier
112762           include.sort(sortFeatures);
112763           var id = '+[' + include.map(function (d) { return d.id; }).join(',') + ']';
112764           if (exclude.length) {
112765             exclude.sort(sortFeatures);
112766             id += '-[' + exclude.map(function (d) { return d.id; }).join(',') + ']';
112767           }
112768
112769           // return cached?
112770           if (this._cache[id]) {
112771             return this._cache[id];
112772           }
112773
112774           // calculate unions
112775           var includeGeoJSON = include.map(function (d) { return d.location; }).reduce(_locationReducer.bind(this), null);
112776           var excludeGeoJSON = exclude.map(function (d) { return d.location; }).reduce(_locationReducer.bind(this), null);
112777
112778           // calculate difference, update area and return result
112779           var resultGeoJSON = excludeGeoJSON ? difference(includeGeoJSON, excludeGeoJSON) : includeGeoJSON;
112780           var area = geojsonArea.geometry(resultGeoJSON.geometry) / 1e6;// m² to km²
112781           resultGeoJSON.id = id;
112782           resultGeoJSON.properties = { id: id, area: Number(area.toFixed(2)) };
112783
112784           return this._cache[id] = resultGeoJSON;
112785
112786
112787           // Sorting the location lists is ok because they end up unioned together.
112788           // This sorting makes it possible to generate a deterministic id.
112789           function sortFeatures(a, b) {
112790             var rank = { countrycoder: 1, geojson: 2, point: 3 };
112791             var aRank = rank[a.type];
112792             var bRank = rank[b.type];
112793
112794             return (aRank > bRank) ? 1
112795               : (aRank < bRank) ? -1
112796               : a.id.localeCompare(b.id);
112797           }
112798         };
112799
112800
112801         defaultExport.prototype.cache = function cache () {
112802           return this._cache;
112803         };
112804
112805         var _oci = null;
112806
112807         function uiSuccess(context) {
112808           var MAXEVENTS = 2;
112809           var dispatch$1 = dispatch('cancel');
112810           var _changeset;
112811           var _location;
112812           ensureOSMCommunityIndex();   // start fetching the data
112813
112814
112815           function ensureOSMCommunityIndex() {
112816             var data = _mainFileFetcher;
112817             return Promise.all([ data.get('oci_resources'), data.get('oci_features') ])
112818               .then(function (vals) {
112819                 if (_oci) { return _oci; }
112820
112821                 var ociResources = vals[0].resources;
112822                 var loco = new defaultExport(vals[1]);
112823                 var ociFeatures = {};
112824
112825                 Object.values(ociResources).forEach(function (resource) {
112826                   var feature = loco.resolveLocationSet(resource.locationSet);
112827                   var ociFeature = ociFeatures[feature.id];
112828                   if (!ociFeature) {
112829                     ociFeature = JSON.parse(JSON.stringify(feature));  // deep clone
112830                     ociFeature.properties.resourceIDs = new Set();
112831                     ociFeatures[feature.id] = ociFeature;
112832                   }
112833                   ociFeature.properties.resourceIDs.add(resource.id);
112834                 });
112835
112836                 return _oci = {
112837                   features: ociFeatures,
112838                   resources: ociResources,
112839                   query: whichPolygon_1({ type: 'FeatureCollection', features: Object.values(ociFeatures) })
112840                 };
112841               });
112842           }
112843
112844
112845           // string-to-date parsing in JavaScript is weird
112846           function parseEventDate(when) {
112847             if (!when) { return; }
112848
112849             var raw = when.trim();
112850             if (!raw) { return; }
112851
112852             if (!/Z$/.test(raw)) {   // if no trailing 'Z', add one
112853               raw += 'Z';            // this forces date to be parsed as a UTC date
112854             }
112855
112856             var parsed = new Date(raw);
112857             return new Date(parsed.toUTCString().substr(0, 25));  // convert to local timezone
112858           }
112859
112860
112861           function success(selection) {
112862             var header = selection
112863               .append('div')
112864               .attr('class', 'header fillL');
112865
112866             header
112867               .append('h3')
112868               .text(_t('success.just_edited'));
112869
112870             header
112871               .append('button')
112872               .attr('class', 'close')
112873               .on('click', function () { return dispatch$1.call('cancel'); })
112874               .call(svgIcon('#iD-icon-close'));
112875
112876             var body = selection
112877               .append('div')
112878               .attr('class', 'body save-success fillL');
112879
112880             var summary = body
112881               .append('div')
112882               .attr('class', 'save-summary');
112883
112884             summary
112885               .append('h3')
112886               .text(_t('success.thank_you' + (_location ? '_location' : ''), { where: _location }));
112887
112888             summary
112889               .append('p')
112890               .text(_t('success.help_html'))
112891               .append('a')
112892               .attr('class', 'link-out')
112893               .attr('target', '_blank')
112894               .attr('tabindex', -1)
112895               .attr('href', _t('success.help_link_url'))
112896               .call(svgIcon('#iD-icon-out-link', 'inline'))
112897               .append('span')
112898               .text(_t('success.help_link_text'));
112899
112900             var osm = context.connection();
112901             if (!osm) { return; }
112902
112903             var changesetURL = osm.changesetURL(_changeset.id);
112904
112905             var table = summary
112906               .append('table')
112907               .attr('class', 'summary-table');
112908
112909             var row = table
112910               .append('tr')
112911               .attr('class', 'summary-row');
112912
112913             row
112914               .append('td')
112915               .attr('class', 'cell-icon summary-icon')
112916               .append('a')
112917               .attr('target', '_blank')
112918               .attr('href', changesetURL)
112919               .append('svg')
112920               .attr('class', 'logo-small')
112921               .append('use')
112922               .attr('xlink:href', '#iD-logo-osm');
112923
112924             var summaryDetail = row
112925               .append('td')
112926               .attr('class', 'cell-detail summary-detail');
112927
112928             summaryDetail
112929               .append('a')
112930               .attr('class', 'cell-detail summary-view-on-osm')
112931               .attr('target', '_blank')
112932               .attr('href', changesetURL)
112933               .text(_t('success.view_on_osm'));
112934
112935             summaryDetail
112936               .append('div')
112937               .html(_t('success.changeset_id', {
112938                 changeset_id: ("<a href=\"" + changesetURL + "\" target=\"_blank\">" + (_changeset.id) + "</a>")
112939               }));
112940
112941
112942             // Get OSM community index features intersecting the map..
112943             ensureOSMCommunityIndex()
112944               .then(function (oci) {
112945                 var communities = [];
112946                 var properties = oci.query(context.map().center(), true) || [];
112947
112948                 // Gather the communities from the result
112949                 properties.forEach(function (props) {
112950                   var resourceIDs = Array.from(props.resourceIDs);
112951                   resourceIDs.forEach(function (resourceID) {
112952                     var resource = oci.resources[resourceID];
112953                     communities.push({
112954                       area: props.area || Infinity,
112955                       order: resource.order || 0,
112956                       resource: resource
112957                     });
112958                   });
112959                 });
112960
112961                 // sort communities by feature area ascending, community order descending
112962                 communities.sort(function (a, b) { return a.area - b.area || b.order - a.order; });
112963
112964                 body
112965                   .call(showCommunityLinks, communities.map(function (c) { return c.resource; }));
112966               });
112967           }
112968
112969
112970           function showCommunityLinks(selection, resources) {
112971             var communityLinks = selection
112972               .append('div')
112973               .attr('class', 'save-communityLinks');
112974
112975             communityLinks
112976               .append('h3')
112977               .text(_t('success.like_osm'));
112978
112979             var table = communityLinks
112980               .append('table')
112981               .attr('class', 'community-table');
112982
112983             var row = table.selectAll('.community-row')
112984               .data(resources);
112985
112986             var rowEnter = row.enter()
112987               .append('tr')
112988               .attr('class', 'community-row');
112989
112990             rowEnter
112991               .append('td')
112992               .attr('class', 'cell-icon community-icon')
112993               .append('a')
112994               .attr('target', '_blank')
112995               .attr('href', function (d) { return d.url; })
112996               .append('svg')
112997               .attr('class', 'logo-small')
112998               .append('use')
112999               .attr('xlink:href', function (d) { return ("#community-" + (d.type)); });
113000
113001             var communityDetail = rowEnter
113002               .append('td')
113003               .attr('class', 'cell-detail community-detail');
113004
113005             communityDetail
113006               .each(showCommunityDetails);
113007
113008             communityLinks
113009               .append('div')
113010               .attr('class', 'community-missing')
113011               .text(_t('success.missing'))
113012               .append('a')
113013               .attr('class', 'link-out')
113014               .attr('target', '_blank')
113015               .attr('tabindex', -1)
113016               .call(svgIcon('#iD-icon-out-link', 'inline'))
113017               .attr('href', 'https://github.com/osmlab/osm-community-index/issues')
113018               .append('span')
113019               .text(_t('success.tell_us'));
113020           }
113021
113022
113023           function showCommunityDetails(d) {
113024             var selection = select(this);
113025             var communityID = d.id;
113026             var replacements = {
113027               url: linkify(d.url),
113028               signupUrl: linkify(d.signupUrl || d.url)
113029             };
113030
113031             selection
113032               .append('div')
113033               .attr('class', 'community-name')
113034               .append('a')
113035               .attr('target', '_blank')
113036               .attr('href', d.url)
113037               .text(_t(("community." + (d.id) + ".name")));
113038
113039             var descriptionHTML = _t(("community." + (d.id) + ".description"), replacements);
113040
113041             if (d.type === 'reddit') {   // linkify subreddits  #4997
113042               descriptionHTML = descriptionHTML
113043                 .replace(/(\/r\/\w*\/*)/i, function (match) { return linkify(d.url, match); });
113044             }
113045
113046             selection
113047               .append('div')
113048               .attr('class', 'community-description')
113049               .html(descriptionHTML);
113050
113051             if (d.extendedDescription || (d.languageCodes && d.languageCodes.length)) {
113052               selection
113053                 .append('div')
113054                 .call(uiDisclosure(context, ("community-more-" + (d.id)), false)
113055                   .expanded(false)
113056                   .updatePreference(false)
113057                   .title(_t('success.more'))
113058                   .content(showMore)
113059                 );
113060             }
113061
113062             var nextEvents = (d.events || [])
113063               .map(function (event) {
113064                 event.date = parseEventDate(event.when);
113065                 return event;
113066               })
113067               .filter(function (event) {      // date is valid and future (or today)
113068                 var t = event.date.getTime();
113069                 var now = (new Date()).setHours(0,0,0,0);
113070                 return !isNaN(t) && t >= now;
113071               })
113072               .sort(function (a, b) {       // sort by date ascending
113073                 return a.date < b.date ? -1 : a.date > b.date ? 1 : 0;
113074               })
113075               .slice(0, MAXEVENTS);   // limit number of events shown
113076
113077             if (nextEvents.length) {
113078               selection
113079                 .append('div')
113080                 .call(uiDisclosure(context, ("community-events-" + (d.id)), false)
113081                   .expanded(false)
113082                   .updatePreference(false)
113083                   .title(_t('success.events'))
113084                   .content(showNextEvents)
113085                 )
113086                 .select('.hide-toggle')
113087                 .append('span')
113088                 .attr('class', 'badge-text')
113089                 .text(nextEvents.length);
113090             }
113091
113092
113093             function showMore(selection) {
113094               var more = selection.selectAll('.community-more')
113095                 .data([0]);
113096
113097               var moreEnter = more.enter()
113098                 .append('div')
113099                 .attr('class', 'community-more');
113100
113101               if (d.extendedDescription) {
113102                 moreEnter
113103                   .append('div')
113104                   .attr('class', 'community-extended-description')
113105                   .html(_t(("community." + (d.id) + ".extendedDescription"), replacements));
113106               }
113107
113108               if (d.languageCodes && d.languageCodes.length) {
113109                 var languageList = d.languageCodes
113110                   .map(function (code) { return _mainLocalizer.languageName(code); })
113111                   .join(', ');
113112
113113                 moreEnter
113114                   .append('div')
113115                   .attr('class', 'community-languages')
113116                   .text(_t('success.languages', { languages: languageList }));
113117               }
113118             }
113119
113120
113121             function showNextEvents(selection) {
113122               var events = selection
113123                 .append('div')
113124                 .attr('class', 'community-events');
113125
113126               var item = events.selectAll('.community-event')
113127                 .data(nextEvents);
113128
113129               var itemEnter = item.enter()
113130                 .append('div')
113131                 .attr('class', 'community-event');
113132
113133               itemEnter
113134                 .append('div')
113135                 .attr('class', 'community-event-name')
113136                 .append('a')
113137                 .attr('target', '_blank')
113138                 .attr('href', function (d) { return d.url; })
113139                 .text(function (d) {
113140                   var name = d.name;
113141                   if (d.i18n && d.id) {
113142                     name = _t(("community." + communityID + ".events." + (d.id) + ".name"), { default: name });
113143                   }
113144                   return name;
113145                 });
113146
113147               itemEnter
113148                 .append('div')
113149                 .attr('class', 'community-event-when')
113150                 .text(function (d) {
113151                   var options = { weekday: 'short', day: 'numeric', month: 'short', year: 'numeric' };
113152                   if (d.date.getHours() || d.date.getMinutes()) {   // include time if it has one
113153                     options.hour = 'numeric';
113154                     options.minute = 'numeric';
113155                   }
113156                   return d.date.toLocaleString(_mainLocalizer.localeCode(), options);
113157                 });
113158
113159               itemEnter
113160                 .append('div')
113161                 .attr('class', 'community-event-where')
113162                 .text(function (d) {
113163                   var where = d.where;
113164                   if (d.i18n && d.id) {
113165                     where = _t(("community." + communityID + ".events." + (d.id) + ".where"), { default: where });
113166                   }
113167                   return where;
113168                 });
113169
113170               itemEnter
113171                 .append('div')
113172                 .attr('class', 'community-event-description')
113173                 .text(function (d) {
113174                   var description = d.description;
113175                   if (d.i18n && d.id) {
113176                     description = _t(("community." + communityID + ".events." + (d.id) + ".description"), { default: description });
113177                   }
113178                   return description;
113179                 });
113180             }
113181
113182
113183             function linkify(url, text) {
113184               text = text || url;
113185               return ("<a target=\"_blank\" href=\"" + url + "\">" + text + "</a>");
113186             }
113187           }
113188
113189
113190           success.changeset = function(val) {
113191             if (!arguments.length) { return _changeset; }
113192             _changeset = val;
113193             return success;
113194           };
113195
113196
113197           success.location = function(val) {
113198             if (!arguments.length) { return _location; }
113199             _location = val;
113200             return success;
113201           };
113202
113203
113204           return utilRebind(success, dispatch$1, 'on');
113205         }
113206
113207         function modeSave(context) {
113208             var mode = { id: 'save' };
113209             var keybinding = utilKeybinding('modeSave');
113210
113211             var commit = uiCommit(context)
113212                 .on('cancel', cancel);
113213             var _conflictsUi; // uiConflicts
113214
113215             var _location;
113216             var _success;
113217
113218             var uploader = context.uploader()
113219                 .on('saveStarted.modeSave', function() {
113220                     keybindingOff();
113221                 })
113222                 // fire off some async work that we want to be ready later
113223                 .on('willAttemptUpload.modeSave', prepareForSuccess)
113224                 .on('progressChanged.modeSave', showProgress)
113225                 .on('resultNoChanges.modeSave', function() {
113226                     cancel();
113227                 })
113228                 .on('resultErrors.modeSave', showErrors)
113229                 .on('resultConflicts.modeSave', showConflicts)
113230                 .on('resultSuccess.modeSave', showSuccess);
113231
113232
113233             function cancel() {
113234                 context.enter(modeBrowse(context));
113235             }
113236
113237
113238             function showProgress(num, total) {
113239                 var modal = context.container().select('.loading-modal .modal-section');
113240                 var progress = modal.selectAll('.progress')
113241                     .data([0]);
113242
113243                 // enter/update
113244                 progress.enter()
113245                     .append('div')
113246                     .attr('class', 'progress')
113247                     .merge(progress)
113248                     .text(_t('save.conflict_progress', { num: num, total: total }));
113249             }
113250
113251
113252             function showConflicts(changeset, conflicts, origChanges) {
113253
113254                 var selection = context.container()
113255                     .select('.sidebar')
113256                     .append('div')
113257                     .attr('class','sidebar-component');
113258
113259                 context.container().selectAll('.main-content')
113260                     .classed('active', true)
113261                     .classed('inactive', false);
113262
113263                 _conflictsUi = uiConflicts(context)
113264                     .conflictList(conflicts)
113265                     .origChanges(origChanges)
113266                     .on('cancel', function() {
113267                         context.container().selectAll('.main-content')
113268                             .classed('active', false)
113269                             .classed('inactive', true);
113270                         selection.remove();
113271                         keybindingOn();
113272
113273                         uploader.cancelConflictResolution();
113274                     })
113275                     .on('save', function() {
113276                         context.container().selectAll('.main-content')
113277                             .classed('active', false)
113278                             .classed('inactive', true);
113279                         selection.remove();
113280
113281                         uploader.processResolvedConflicts(changeset);
113282                     });
113283
113284                 selection.call(_conflictsUi);
113285             }
113286
113287
113288             function showErrors(errors) {
113289                 keybindingOn();
113290
113291                 var selection = uiConfirm(context.container());
113292                 selection
113293                     .select('.modal-section.header')
113294                     .append('h3')
113295                     .text(_t('save.error'));
113296
113297                 addErrors(selection, errors);
113298                 selection.okButton();
113299             }
113300
113301
113302             function addErrors(selection, data) {
113303                 var message = selection
113304                     .select('.modal-section.message-text');
113305
113306                 var items = message
113307                     .selectAll('.error-container')
113308                     .data(data);
113309
113310                 var enter = items.enter()
113311                     .append('div')
113312                     .attr('class', 'error-container');
113313
113314                 enter
113315                     .append('a')
113316                     .attr('class', 'error-description')
113317                     .attr('href', '#')
113318                     .classed('hide-toggle', true)
113319                     .text(function(d) { return d.msg || _t('save.unknown_error_details'); })
113320                     .on('click', function() {
113321                         event.preventDefault();
113322
113323                         var error = select(this);
113324                         var detail = select(this.nextElementSibling);
113325                         var exp = error.classed('expanded');
113326
113327                         detail.style('display', exp ? 'none' : 'block');
113328                         error.classed('expanded', !exp);
113329                     });
113330
113331                 var details = enter
113332                     .append('div')
113333                     .attr('class', 'error-detail-container')
113334                     .style('display', 'none');
113335
113336                 details
113337                     .append('ul')
113338                     .attr('class', 'error-detail-list')
113339                     .selectAll('li')
113340                     .data(function(d) { return d.details || []; })
113341                     .enter()
113342                     .append('li')
113343                     .attr('class', 'error-detail-item')
113344                     .text(function(d) { return d; });
113345
113346                 items.exit()
113347                     .remove();
113348             }
113349
113350
113351             function showSuccess(changeset) {
113352                 commit.reset();
113353
113354                 var ui = _success
113355                     .changeset(changeset)
113356                     .location(_location)
113357                     .on('cancel', function() { context.ui().sidebar.hide(); });
113358
113359                 context.enter(modeBrowse(context).sidebar(ui));
113360             }
113361
113362
113363             function keybindingOn() {
113364                 select(document)
113365                     .call(keybinding.on('⎋', cancel, true));
113366             }
113367
113368
113369             function keybindingOff() {
113370                 select(document)
113371                     .call(keybinding.unbind);
113372             }
113373
113374
113375             // Reverse geocode current map location so we can display a message on
113376             // the success screen like "Thank you for editing around place, region."
113377             function prepareForSuccess() {
113378                 _success = uiSuccess(context);
113379                 _location = null;
113380                 if (!services.geocoder) { return; }
113381
113382                 services.geocoder.reverse(context.map().center(), function(err, result) {
113383                     if (err || !result || !result.address) { return; }
113384
113385                     var addr = result.address;
113386                     var place = (addr && (addr.town || addr.city || addr.county)) || '';
113387                     var region = (addr && (addr.state || addr.country)) || '';
113388                     var separator = (place && region) ? _t('success.thank_you_where.separator') : '';
113389
113390                     _location = _t('success.thank_you_where.format',
113391                         { place: place, separator: separator, region: region }
113392                     );
113393                 });
113394             }
113395
113396
113397             mode.selectedIDs = function() {
113398                 return _conflictsUi ? _conflictsUi.shownEntityIds() : [];
113399             };
113400
113401
113402             mode.enter = function() {
113403                 // Show sidebar
113404                 context.ui().sidebar.expand();
113405
113406                 function done() {
113407                     context.ui().sidebar.show(commit);
113408                 }
113409
113410                 keybindingOn();
113411
113412                 context.container().selectAll('.main-content')
113413                     .classed('active', false)
113414                     .classed('inactive', true);
113415
113416                 var osm = context.connection();
113417                 if (!osm) {
113418                     cancel();
113419                     return;
113420                 }
113421
113422                 if (osm.authenticated()) {
113423                     done();
113424                 } else {
113425                     osm.authenticate(function(err) {
113426                         if (err) {
113427                             cancel();
113428                         } else {
113429                             done();
113430                         }
113431                     });
113432                 }
113433             };
113434
113435
113436             mode.exit = function() {
113437
113438                 keybindingOff();
113439
113440                 context.container().selectAll('.main-content')
113441                     .classed('active', true)
113442                     .classed('inactive', false);
113443
113444                 context.ui().sidebar.hide();
113445             };
113446
113447             return mode;
113448         }
113449
113450         function uiToolOldDrawModes(context) {
113451
113452             var tool = {
113453                 id: 'old_modes',
113454                 label: _t('toolbar.add_feature')
113455             };
113456
113457             var modes = [
113458                 modeAddPoint(context, {
113459                     title: _t('modes.add_point.title'),
113460                     button: 'point',
113461                     description: _t('modes.add_point.description'),
113462                     preset: _mainPresetIndex.item('point'),
113463                     key: '1'
113464                 }),
113465                 modeAddLine(context, {
113466                     title: _t('modes.add_line.title'),
113467                     button: 'line',
113468                     description: _t('modes.add_line.description'),
113469                     preset: _mainPresetIndex.item('line'),
113470                     key: '2'
113471                 }),
113472                 modeAddArea(context, {
113473                     title: _t('modes.add_area.title'),
113474                     button: 'area',
113475                     description: _t('modes.add_area.description'),
113476                     preset: _mainPresetIndex.item('area'),
113477                     key: '3'
113478                 })
113479             ];
113480
113481
113482             function enabled() {
113483                 return osmEditable();
113484             }
113485
113486             function osmEditable() {
113487                 return context.editable();
113488             }
113489
113490             modes.forEach(function(mode) {
113491                 context.keybinding().on(mode.key, function() {
113492                     if (!enabled()) { return; }
113493
113494                     if (mode.id === context.mode().id) {
113495                         context.enter(modeBrowse(context));
113496                     } else {
113497                         context.enter(mode);
113498                     }
113499                 });
113500             });
113501
113502             tool.render = function(selection) {
113503
113504                 var wrap = selection
113505                     .append('div')
113506                     .attr('class', 'joined')
113507                     .style('display', 'flex');
113508
113509                 var debouncedUpdate = debounce(update, 500, { leading: true, trailing: true });
113510
113511                 context.map()
113512                     .on('move.modes', debouncedUpdate)
113513                     .on('drawn.modes', debouncedUpdate);
113514
113515                 context
113516                     .on('enter.modes', update);
113517
113518                 update();
113519
113520
113521                 function update() {
113522
113523                     var buttons = wrap.selectAll('button.add-button')
113524                         .data(modes, function(d) { return d.id; });
113525
113526                     // exit
113527                     buttons.exit()
113528                         .remove();
113529
113530                     // enter
113531                     var buttonsEnter = buttons.enter()
113532                         .append('button')
113533                         .attr('class', function(d) { return d.id + ' add-button bar-button'; })
113534                         .on('click.mode-buttons', function(d) {
113535                             if (!enabled()) { return; }
113536
113537                             // When drawing, ignore accidental clicks on mode buttons - #4042
113538                             var currMode = context.mode().id;
113539                             if (/^draw/.test(currMode)) { return; }
113540
113541                             if (d.id === currMode) {
113542                                 context.enter(modeBrowse(context));
113543                             } else {
113544                                 context.enter(d);
113545                             }
113546                         })
113547                         .call(uiTooltip()
113548                             .placement('bottom')
113549                             .title(function(d) { return d.description; })
113550                             .keys(function(d) { return [d.key]; })
113551                             .scrollContainer(context.container().select('.top-toolbar'))
113552                         );
113553
113554                     buttonsEnter
113555                         .each(function(d) {
113556                             select(this)
113557                                 .call(svgIcon('#iD-icon-' + d.button));
113558                         });
113559
113560                     buttonsEnter
113561                         .append('span')
113562                         .attr('class', 'label')
113563                         .text(function(mode) { return mode.title; });
113564
113565                     // if we are adding/removing the buttons, check if toolbar has overflowed
113566                     if (buttons.enter().size() || buttons.exit().size()) {
113567                         context.ui().checkOverflow('.top-toolbar', true);
113568                     }
113569
113570                     // update
113571                     buttons = buttons
113572                         .merge(buttonsEnter)
113573                         .classed('disabled', function(d) { return !enabled(); })
113574                         .classed('active', function(d) { return context.mode() && context.mode().button === d.button; });
113575                 }
113576             };
113577
113578             return tool;
113579         }
113580
113581         function uiToolNotes(context) {
113582
113583             var tool = {
113584                 id: 'notes',
113585                 label: _t('modes.add_note.label')
113586             };
113587
113588             var mode = modeAddNote(context);
113589
113590             function enabled() {
113591                 return notesEnabled() && notesEditable();
113592             }
113593
113594             function notesEnabled() {
113595                 var noteLayer = context.layers().layer('notes');
113596                 return noteLayer && noteLayer.enabled();
113597             }
113598
113599             function notesEditable() {
113600                 var mode = context.mode();
113601                 return context.map().notesEditable() && mode && mode.id !== 'save';
113602             }
113603
113604             context.keybinding().on(mode.key, function() {
113605                 if (!enabled()) { return; }
113606
113607                 if (mode.id === context.mode().id) {
113608                     context.enter(modeBrowse(context));
113609                 } else {
113610                     context.enter(mode);
113611                 }
113612             });
113613
113614             tool.render = function(selection) {
113615
113616
113617                 var debouncedUpdate = debounce(update, 500, { leading: true, trailing: true });
113618
113619                 context.map()
113620                     .on('move.notes', debouncedUpdate)
113621                     .on('drawn.notes', debouncedUpdate);
113622
113623                 context
113624                     .on('enter.notes', update);
113625
113626                 update();
113627
113628
113629                 function update() {
113630                     var showNotes = notesEnabled();
113631                     var data = showNotes ? [mode] : [];
113632
113633                     var buttons = selection.selectAll('button.add-button')
113634                         .data(data, function(d) { return d.id; });
113635
113636                     // exit
113637                     buttons.exit()
113638                         .remove();
113639
113640                     // enter
113641                     var buttonsEnter = buttons.enter()
113642                         .append('button')
113643                         .attr('tabindex', -1)
113644                         .attr('class', function(d) { return d.id + ' add-button bar-button'; })
113645                         .on('click.notes', function(d) {
113646                             if (!enabled()) { return; }
113647
113648                             // When drawing, ignore accidental clicks on mode buttons - #4042
113649                             var currMode = context.mode().id;
113650                             if (/^draw/.test(currMode)) { return; }
113651
113652                             if (d.id === currMode) {
113653                                 context.enter(modeBrowse(context));
113654                             } else {
113655                                 context.enter(d);
113656                             }
113657                         })
113658                         .call(uiTooltip()
113659                             .placement('bottom')
113660                             .title(function(d) { return d.description; })
113661                             .keys(function(d) { return [d.key]; })
113662                             .scrollContainer(context.container().select('.top-toolbar'))
113663                         );
113664
113665                     buttonsEnter
113666                         .each(function(d) {
113667                             select(this)
113668                                 .call(svgIcon(d.icon || '#iD-icon-' + d.button));
113669                         });
113670
113671                     // if we are adding/removing the buttons, check if toolbar has overflowed
113672                     if (buttons.enter().size() || buttons.exit().size()) {
113673                         context.ui().checkOverflow('.top-toolbar', true);
113674                     }
113675
113676                     // update
113677                     buttons = buttons
113678                         .merge(buttonsEnter)
113679                         .classed('disabled', function(d) { return !enabled(); })
113680                         .classed('active', function(d) { return context.mode() && context.mode().button === d.button; });
113681                 }
113682             };
113683
113684             tool.uninstall = function() {
113685                 context
113686                     .on('enter.editor.notes', null)
113687                     .on('exit.editor.notes', null)
113688                     .on('enter.notes', null);
113689
113690                 context.map()
113691                     .on('move.notes', null)
113692                     .on('drawn.notes', null);
113693             };
113694
113695             return tool;
113696         }
113697
113698         function uiToolSave(context) {
113699
113700             var tool = {
113701                 id: 'save',
113702                 label: _t('save.title')
113703             };
113704
113705             var button = null;
113706             var tooltipBehavior = null;
113707             var history = context.history();
113708             var key = uiCmd('⌘S');
113709             var _numChanges = 0;
113710
113711             function isSaving() {
113712                 var mode = context.mode();
113713                 return mode && mode.id === 'save';
113714             }
113715
113716             function isDisabled() {
113717                 return _numChanges === 0 || isSaving();
113718             }
113719
113720             function save() {
113721                 event.preventDefault();
113722                 if (!context.inIntro() && !isSaving() && history.hasChanges()) {
113723                     context.enter(modeSave(context));
113724                 }
113725             }
113726
113727             function bgColor() {
113728                 var step;
113729                 if (_numChanges === 0) {
113730                     return null;
113731                 } else if (_numChanges <= 50) {
113732                     step = _numChanges / 50;
113733                     return d3_interpolateRgb('#fff', '#ff8')(step);  // white -> yellow
113734                 } else {
113735                     step = Math.min((_numChanges - 50) / 50, 1.0);
113736                     return d3_interpolateRgb('#ff8', '#f88')(step);  // yellow -> red
113737                 }
113738             }
113739
113740             function updateCount() {
113741                 var val = history.difference().summary().length;
113742                 if (val === _numChanges) { return; }
113743
113744                 _numChanges = val;
113745
113746                 if (tooltipBehavior) {
113747                     tooltipBehavior
113748                         .title(_t(_numChanges > 0 ? 'save.help' : 'save.no_changes'))
113749                         .keys([key]);
113750                 }
113751
113752                 if (button) {
113753                     button
113754                         .classed('disabled', isDisabled())
113755                         .style('background', bgColor());
113756
113757                     button.select('span.count')
113758                         .text(_numChanges);
113759                 }
113760             }
113761
113762
113763             tool.render = function(selection) {
113764                 tooltipBehavior = uiTooltip()
113765                     .placement('bottom')
113766                     .title(_t('save.no_changes'))
113767                     .keys([key])
113768                     .scrollContainer(context.container().select('.top-toolbar'));
113769
113770                 var lastPointerUpType;
113771
113772                 button = selection
113773                     .append('button')
113774                     .attr('class', 'save disabled bar-button')
113775                     .on('pointerup', function() {
113776                         lastPointerUpType = event.pointerType;
113777                     })
113778                     .on('click', function() {
113779                         event.preventDefault();
113780
113781                         save();
113782
113783                         if (_numChanges === 0 && (
113784                             lastPointerUpType === 'touch' ||
113785                             lastPointerUpType === 'pen')
113786                         ) {
113787                             // there are no tooltips for touch interactions so flash feedback instead
113788                             context.ui().flash
113789                                 .duration(2000)
113790                                 .iconName('#iD-icon-save')
113791                                 .iconClass('disabled')
113792                                 .text(_t('save.no_changes'))();
113793                         }
113794                         lastPointerUpType = null;
113795                     })
113796                     .call(tooltipBehavior);
113797
113798                 button
113799                     .call(svgIcon('#iD-icon-save'));
113800
113801                 button
113802                     .append('span')
113803                     .attr('class', 'count')
113804                     .attr('aria-hidden', 'true')
113805                     .text('0');
113806
113807                 updateCount();
113808
113809
113810                 context.keybinding()
113811                     .on(key, save, true);
113812
113813
113814                 context.history()
113815                     .on('change.save', updateCount);
113816
113817                 context
113818                     .on('enter.save', function() {
113819                         if (button) {
113820                             button
113821                                 .classed('disabled', isDisabled());
113822
113823                             if (isSaving()) {
113824                                 button.call(tooltipBehavior.hide);
113825                             }
113826                         }
113827                     });
113828             };
113829
113830
113831             tool.uninstall = function() {
113832                 context.keybinding()
113833                     .off(key, true);
113834
113835                 context.history()
113836                     .on('change.save', null);
113837
113838                 context
113839                     .on('enter.save', null);
113840
113841                 button = null;
113842                 tooltipBehavior = null;
113843             };
113844
113845             return tool;
113846         }
113847
113848         function uiToolSidebarToggle(context) {
113849
113850             var tool = {
113851                 id: 'sidebar_toggle',
113852                 label: _t('toolbar.inspect')
113853             };
113854
113855             tool.render = function(selection) {
113856                 selection
113857                     .append('button')
113858                     .attr('class', 'bar-button')
113859                     .on('click', function() {
113860                         context.ui().sidebar.toggle();
113861                     })
113862                     .call(uiTooltip()
113863                         .placement('bottom')
113864                         .title(_t('sidebar.tooltip'))
113865                         .keys([_t('sidebar.key')])
113866                         .scrollContainer(context.container().select('.top-toolbar'))
113867                     )
113868                     .call(svgIcon('#iD-icon-sidebar-' + (_mainLocalizer.textDirection() === 'rtl' ? 'right' : 'left')));
113869             };
113870
113871             return tool;
113872         }
113873
113874         function uiToolUndoRedo(context) {
113875
113876             var tool = {
113877                 id: 'undo_redo',
113878                 label: _t('toolbar.undo_redo')
113879             };
113880
113881             var commands = [{
113882                 id: 'undo',
113883                 cmd: uiCmd('⌘Z'),
113884                 action: function() {
113885                     context.undo();
113886                 },
113887                 annotation: function() {
113888                     return context.history().undoAnnotation();
113889                 },
113890                 icon: 'iD-icon-' + (_mainLocalizer.textDirection() === 'rtl' ? 'redo' : 'undo')
113891             }, {
113892                 id: 'redo',
113893                 cmd: uiCmd('⌘⇧Z'),
113894                 action: function() {
113895                     context.redo();
113896                 },
113897                 annotation: function() {
113898                     return context.history().redoAnnotation();
113899                 },
113900                 icon: 'iD-icon-' + (_mainLocalizer.textDirection() === 'rtl' ? 'undo' : 'redo')
113901             }];
113902
113903
113904             function editable() {
113905                 return context.mode() && context.mode().id !== 'save' && context.map().editableDataEnabled(true /* ignore min zoom */);
113906             }
113907
113908
113909             tool.render = function(selection) {
113910                 var tooltipBehavior = uiTooltip()
113911                     .placement('bottom')
113912                     .title(function (d) {
113913                         return d.annotation() ?
113914                             _t(d.id + '.tooltip', { action: d.annotation() }) :
113915                             _t(d.id + '.nothing');
113916                     })
113917                     .keys(function(d) {
113918                         return [d.cmd];
113919                     })
113920                     .scrollContainer(context.container().select('.top-toolbar'));
113921
113922                 var lastPointerUpType;
113923
113924                 var buttons = selection.selectAll('button')
113925                     .data(commands)
113926                     .enter()
113927                     .append('button')
113928                     .attr('class', function(d) { return 'disabled ' + d.id + '-button bar-button'; })
113929                     .on('pointerup', function() {
113930                         // `pointerup` is always called before `click`
113931                         lastPointerUpType = event.pointerType;
113932                     })
113933                     .on('click', function(d) {
113934                         event.preventDefault();
113935
113936                         var annotation = d.annotation();
113937
113938                         if (editable() && annotation) {
113939                             d.action();
113940                         }
113941
113942                         if (editable() && (
113943                             lastPointerUpType === 'touch' ||
113944                             lastPointerUpType === 'pen')
113945                         ) {
113946                             // there are no tooltips for touch interactions so flash feedback instead
113947
113948                             var text = annotation ?
113949                                 _t(d.id + '.tooltip', { action: annotation }) :
113950                                 _t(d.id + '.nothing');
113951                             context.ui().flash
113952                                 .duration(2000)
113953                                 .iconName('#' + d.icon)
113954                                 .iconClass(annotation ? '' : 'disabled')
113955                                 .text(text)();
113956                         }
113957                         lastPointerUpType = null;
113958                     })
113959                     .call(tooltipBehavior);
113960
113961                 buttons.each(function(d) {
113962                     select(this)
113963                         .call(svgIcon('#' + d.icon));
113964                 });
113965
113966                 context.keybinding()
113967                     .on(commands[0].cmd, function() {
113968                         event.preventDefault();
113969                         if (editable()) { commands[0].action(); }
113970                     })
113971                     .on(commands[1].cmd, function() {
113972                         event.preventDefault();
113973                         if (editable()) { commands[1].action(); }
113974                     });
113975
113976
113977                 var debouncedUpdate = debounce(update, 500, { leading: true, trailing: true });
113978
113979                 context.map()
113980                     .on('move.undo_redo', debouncedUpdate)
113981                     .on('drawn.undo_redo', debouncedUpdate);
113982
113983                 context.history()
113984                     .on('change.undo_redo', function(difference) {
113985                         if (difference) { update(); }
113986                     });
113987
113988                 context
113989                     .on('enter.undo_redo', update);
113990
113991
113992                 function update() {
113993                     buttons
113994                         .classed('disabled', function(d) {
113995                             return !editable() || !d.annotation();
113996                         })
113997                         .each(function() {
113998                             var selection = select(this);
113999                             if (!selection.select('.tooltip.in').empty()) {
114000                                 selection.call(tooltipBehavior.updateContent);
114001                             }
114002                         });
114003                 }
114004             };
114005
114006             tool.uninstall = function() {
114007                 context.keybinding()
114008                     .off(commands[0].cmd)
114009                     .off(commands[1].cmd);
114010
114011                 context.map()
114012                     .on('move.undo_redo', null)
114013                     .on('drawn.undo_redo', null);
114014
114015                 context.history()
114016                     .on('change.undo_redo', null);
114017
114018                 context
114019                     .on('enter.undo_redo', null);
114020             };
114021
114022             return tool;
114023         }
114024
114025         function uiTopToolbar(context) {
114026
114027             var sidebarToggle = uiToolSidebarToggle(context),
114028                 modes = uiToolOldDrawModes(context),
114029                 notes = uiToolNotes(context),
114030                 undoRedo = uiToolUndoRedo(context),
114031                 save = uiToolSave(context);
114032
114033             function notesEnabled() {
114034                 var noteLayer = context.layers().layer('notes');
114035                 return noteLayer && noteLayer.enabled();
114036             }
114037
114038             function topToolbar(bar) {
114039
114040                 bar.on('wheel.topToolbar', function() {
114041                     if (!event.deltaX) {
114042                         // translate vertical scrolling into horizontal scrolling in case
114043                         // the user doesn't have an input device that can scroll horizontally
114044                         bar.node().scrollLeft += event.deltaY;
114045                     }
114046                 });
114047
114048                 var debouncedUpdate = debounce(update, 500, { leading: true, trailing: true });
114049                 context.layers()
114050                     .on('change.topToolbar', debouncedUpdate);
114051
114052                 update();
114053
114054                 function update() {
114055
114056                     var tools = [
114057                         sidebarToggle,
114058                         'spacer',
114059                         modes
114060                     ];
114061
114062                     tools.push('spacer');
114063
114064                     if (notesEnabled()) {
114065                         tools = tools.concat([notes, 'spacer']);
114066                     }
114067
114068                     tools = tools.concat([undoRedo, save]);
114069
114070                     var toolbarItems = bar.selectAll('.toolbar-item')
114071                         .data(tools, function(d) {
114072                             return d.id || d;
114073                         });
114074
114075                     toolbarItems.exit()
114076                         .each(function(d) {
114077                             if (d.uninstall) {
114078                                 d.uninstall();
114079                             }
114080                         })
114081                         .remove();
114082
114083                     var itemsEnter = toolbarItems
114084                         .enter()
114085                         .append('div')
114086                         .attr('class', function(d) {
114087                             var classes = 'toolbar-item ' + (d.id || d).replace('_', '-');
114088                             if (d.klass) { classes += ' ' + d.klass; }
114089                             return classes;
114090                         });
114091
114092                     var actionableItems = itemsEnter.filter(function(d) { return d !== 'spacer'; });
114093
114094                     actionableItems
114095                         .append('div')
114096                         .attr('class', 'item-content')
114097                         .each(function(d) {
114098                             select(this).call(d.render, bar);
114099                         });
114100
114101                     actionableItems
114102                         .append('div')
114103                         .attr('class', 'item-label')
114104                         .text(function(d) {
114105                             return d.label;
114106                         });
114107                 }
114108
114109             }
114110
114111             return topToolbar;
114112         }
114113
114114         // these are module variables so they are preserved through a ui.restart()
114115         var sawVersion = null;
114116         var isNewVersion = false;
114117         var isNewUser = false;
114118
114119
114120         function uiVersion(context) {
114121
114122             var currVersion = context.version;
114123             var matchedVersion = currVersion.match(/\d+\.\d+\.\d+.*/);
114124
114125             if (sawVersion === null && matchedVersion !== null) {
114126                 if (corePreferences('sawVersion')) {
114127                     isNewUser = false;
114128                     isNewVersion = corePreferences('sawVersion') !== currVersion && currVersion.indexOf('-') === -1;
114129                 } else {
114130                     isNewUser = true;
114131                     isNewVersion = true;
114132                 }
114133                 corePreferences('sawVersion', currVersion);
114134                 sawVersion = currVersion;
114135             }
114136
114137             return function(selection) {
114138                 selection
114139                     .append('a')
114140                     .attr('target', '_blank')
114141                     .attr('href', 'https://github.com/openstreetmap/iD')
114142                     .text(currVersion);
114143
114144                 // only show new version indicator to users that have used iD before
114145                 if (isNewVersion && !isNewUser) {
114146                     selection
114147                         .append('div')
114148                         .attr('class', 'badge')
114149                         .append('a')
114150                         .attr('target', '_blank')
114151                         .attr('href', 'https://github.com/openstreetmap/iD/blob/release/CHANGELOG.md#whats-new')
114152                         .call(svgIcon('#maki-gift-11'))
114153                         .call(uiTooltip()
114154                             .title(_t('version.whats_new', { version: currVersion }))
114155                             .placement('top')
114156                         );
114157                 }
114158             };
114159         }
114160
114161         function uiZoom(context) {
114162
114163             var zooms = [{
114164                 id: 'zoom-in',
114165                 icon: 'iD-icon-plus',
114166                 title: _t('zoom.in'),
114167                 action: zoomIn,
114168                 disabled: function() {
114169                     return !context.map().canZoomIn();
114170                 },
114171                 disabledTitle: _t('zoom.disabled.in'),
114172                 key: '+'
114173             }, {
114174                 id: 'zoom-out',
114175                 icon: 'iD-icon-minus',
114176                 title: _t('zoom.out'),
114177                 action: zoomOut,
114178                 disabled: function() {
114179                     return !context.map().canZoomOut();
114180                 },
114181                 disabledTitle: _t('zoom.disabled.out'),
114182                 key: '-'
114183             }];
114184
114185             function zoomIn() {
114186                 event.preventDefault();
114187                 context.map().zoomIn();
114188             }
114189
114190             function zoomOut() {
114191                 event.preventDefault();
114192                 context.map().zoomOut();
114193             }
114194
114195             function zoomInFurther() {
114196                 event.preventDefault();
114197                 context.map().zoomInFurther();
114198             }
114199
114200             function zoomOutFurther() {
114201                 event.preventDefault();
114202                 context.map().zoomOutFurther();
114203             }
114204
114205             return function(selection) {
114206                 var tooltipBehavior = uiTooltip()
114207                     .placement((_mainLocalizer.textDirection() === 'rtl') ? 'right' : 'left')
114208                     .title(function(d) {
114209                         if (d.disabled()) {
114210                             return d.disabledTitle;
114211                         }
114212                         return d.title;
114213                     })
114214                     .keys(function(d) {
114215                         return [d.key];
114216                     });
114217
114218                 var lastPointerUpType;
114219
114220                 var buttons = selection.selectAll('button')
114221                     .data(zooms)
114222                     .enter()
114223                     .append('button')
114224                     .attr('class', function(d) { return d.id; })
114225                     .on('pointerup.editor', function() {
114226                         lastPointerUpType = event.pointerType;
114227                     })
114228                     .on('click.editor', function(d) {
114229                         if (!d.disabled()) {
114230                             d.action();
114231                         } else if (lastPointerUpType === 'touch' || lastPointerUpType === 'pen') {
114232                             context.ui().flash
114233                                 .duration(2000)
114234                                 .iconName('#' + d.icon)
114235                                 .iconClass('disabled')
114236                                 .text(d.disabledTitle)();
114237                         }
114238                         lastPointerUpType = null;
114239                     })
114240                     .call(tooltipBehavior);
114241
114242                 buttons.each(function(d) {
114243                     select(this)
114244                         .call(svgIcon('#' + d.icon, 'light'));
114245                 });
114246
114247                 ['plus', 'ffplus', '=', 'ffequals'].forEach(function(key) {
114248                     context.keybinding().on([key], zoomIn);
114249                     context.keybinding().on([uiCmd('⌘' + key)], zoomInFurther);
114250                 });
114251
114252                 ['_', '-', 'ffminus', 'dash'].forEach(function(key) {
114253                     context.keybinding().on([key], zoomOut);
114254                     context.keybinding().on([uiCmd('⌘' + key)], zoomOutFurther);
114255                 });
114256
114257                 function updateButtonStates() {
114258                     buttons
114259                         .classed('disabled', function(d) {
114260                             return d.disabled();
114261                         })
114262                         .each(function() {
114263                             var selection = select(this);
114264                             if (!selection.select('.tooltip.in').empty()) {
114265                                 selection.call(tooltipBehavior.updateContent);
114266                             }
114267                         });
114268                 }
114269
114270                 updateButtonStates();
114271
114272                 context.map().on('move.uiZoom', updateButtonStates);
114273             };
114274         }
114275
114276         function uiZoomToSelection(context) {
114277
114278             function isDisabled() {
114279                 var mode = context.mode();
114280                 return !mode || !mode.zoomToSelected;
114281             }
114282
114283             var _lastPointerUpType;
114284
114285             function pointerup() {
114286                 _lastPointerUpType = event.pointerType;
114287             }
114288
114289             function click() {
114290                 event.preventDefault();
114291
114292                 if (isDisabled()) {
114293                     if (_lastPointerUpType === 'touch' || _lastPointerUpType === 'pen') {
114294                         context.ui().flash
114295                             .duration(2000)
114296                             .iconName('#iD-icon-framed-dot')
114297                             .iconClass('disabled')
114298                             .text(_t('inspector.zoom_to.no_selection'))();
114299                     }
114300                 } else {
114301                     var mode = context.mode();
114302                     if (mode && mode.zoomToSelected) {
114303                         mode.zoomToSelected();
114304                     }
114305                 }
114306
114307                 _lastPointerUpType = null;
114308             }
114309
114310             return function(selection) {
114311
114312                 var tooltipBehavior = uiTooltip()
114313                     .placement((_mainLocalizer.textDirection() === 'rtl') ? 'right' : 'left')
114314                     .title(function() {
114315                         if (isDisabled()) {
114316                             return _t('inspector.zoom_to.no_selection');
114317                         }
114318                         return _t('inspector.zoom_to.title');
114319                     })
114320                     .keys([_t('inspector.zoom_to.key')]);
114321
114322                 var button = selection
114323                     .append('button')
114324                     .on('pointerup', pointerup)
114325                     .on('click', click)
114326                     .call(svgIcon('#iD-icon-framed-dot', 'light'))
114327                     .call(tooltipBehavior);
114328
114329                 function setEnabledState() {
114330                     button.classed('disabled', isDisabled());
114331                     if (!button.select('.tooltip.in').empty()) {
114332                         button.call(tooltipBehavior.updateContent);
114333                     }
114334                 }
114335
114336                 context.on('enter.uiZoomToSelection', setEnabledState);
114337
114338                 setEnabledState();
114339             };
114340         }
114341
114342         function uiPane(id, context) {
114343
114344             var _key;
114345             var _title = '';
114346             var _description = '';
114347             var _iconName = '';
114348             var _sections; // array of uiSection objects
114349
114350             var _paneSelection = select(null);
114351
114352             var _paneTooltip;
114353
114354             var pane = {
114355                 id: id
114356             };
114357
114358             pane.title = function(val) {
114359                 if (!arguments.length) { return _title; }
114360                 _title = val;
114361                 return pane;
114362             };
114363
114364             pane.key = function(val) {
114365                 if (!arguments.length) { return _key; }
114366                 _key = val;
114367                 return pane;
114368             };
114369
114370             pane.description = function(val) {
114371                 if (!arguments.length) { return _description; }
114372                 _description = val;
114373                 return pane;
114374             };
114375
114376             pane.iconName = function(val) {
114377                 if (!arguments.length) { return _iconName; }
114378                 _iconName = val;
114379                 return pane;
114380             };
114381
114382             pane.sections = function(val) {
114383                 if (!arguments.length) { return _sections; }
114384                 _sections = val;
114385                 return pane;
114386             };
114387
114388             pane.selection = function() {
114389                 return _paneSelection;
114390             };
114391
114392             function hidePane() {
114393                 context.ui().togglePanes();
114394             }
114395
114396             pane.togglePane = function() {
114397                 if (event) { event.preventDefault(); }
114398                 _paneTooltip.hide();
114399                 context.ui().togglePanes(!_paneSelection.classed('shown') ? _paneSelection : undefined);
114400             };
114401
114402             pane.renderToggleButton = function(selection) {
114403
114404                 if (!_paneTooltip) {
114405                     _paneTooltip = uiTooltip()
114406                         .placement((_mainLocalizer.textDirection() === 'rtl') ? 'right' : 'left')
114407                         .title(_description)
114408                         .keys([_key]);
114409                 }
114410
114411                 selection
114412                     .append('button')
114413                     .on('click', pane.togglePane)
114414                     .call(svgIcon('#' + _iconName, 'light'))
114415                     .call(_paneTooltip);
114416             };
114417
114418             pane.renderContent = function(selection) {
114419                 // override to fully customize content
114420
114421                 if (_sections) {
114422                     _sections.forEach(function(section) {
114423                         selection.call(section.render);
114424                     });
114425                 }
114426             };
114427
114428             pane.renderPane = function(selection) {
114429
114430                 _paneSelection = selection
114431                     .append('div')
114432                     .attr('class', 'fillL map-pane hide ' + id + '-pane')
114433                     .attr('pane', id);
114434
114435                 var heading = _paneSelection
114436                     .append('div')
114437                     .attr('class', 'pane-heading');
114438
114439                 heading
114440                     .append('h2')
114441                     .text(_title);
114442
114443                 heading
114444                     .append('button')
114445                     .on('click', hidePane)
114446                     .call(svgIcon('#iD-icon-close'));
114447
114448
114449                 _paneSelection
114450                     .append('div')
114451                     .attr('class', 'pane-content')
114452                     .call(pane.renderContent);
114453
114454                 if (_key) {
114455                     context.keybinding()
114456                         .on(_key, pane.togglePane);
114457                 }
114458             };
114459
114460             return pane;
114461         }
114462
114463         function uiSectionBackgroundDisplayOptions(context) {
114464
114465             var section = uiSection('background-display-options', context)
114466                 .title(_t('background.display_options'))
114467                 .disclosureContent(renderDisclosureContent);
114468
114469             var _detected = utilDetect();
114470             var _storedOpacity = corePreferences('background-opacity');
114471             var _minVal = 0.25;
114472             var _maxVal = _detected.cssfilters ? 2 : 1;
114473
114474             var _sliders = _detected.cssfilters
114475                 ? ['brightness', 'contrast', 'saturation', 'sharpness']
114476                 : ['brightness'];
114477
114478             var _options = {
114479                 brightness: (_storedOpacity !== null ? (+_storedOpacity) : 1),
114480                 contrast: 1,
114481                 saturation: 1,
114482                 sharpness: 1
114483             };
114484
114485             function clamp(x, min, max) {
114486                 return Math.max(min, Math.min(x, max));
114487             }
114488
114489             function updateValue(d, val) {
114490                 if (!val && event && event.target) {
114491                     val = event.target.value;
114492                 }
114493
114494                 val = clamp(val, _minVal, _maxVal);
114495
114496                 _options[d] = val;
114497                 context.background()[d](val);
114498
114499                 if (d === 'brightness') {
114500                     corePreferences('background-opacity', val);
114501                 }
114502
114503                 section.reRender();
114504             }
114505
114506             function renderDisclosureContent(selection) {
114507                 var container = selection.selectAll('.display-options-container')
114508                     .data([0]);
114509
114510                 var containerEnter = container.enter()
114511                     .append('div')
114512                     .attr('class', 'display-options-container controls-list');
114513
114514                 // add slider controls
114515                 var slidersEnter = containerEnter.selectAll('.display-control')
114516                     .data(_sliders)
114517                     .enter()
114518                     .append('div')
114519                     .attr('class', function(d) { return 'display-control display-control-' + d; });
114520
114521                 slidersEnter
114522                     .append('h5')
114523                     .text(function(d) { return _t('background.' + d); })
114524                     .append('span')
114525                     .attr('class', function(d) { return 'display-option-value display-option-value-' + d; });
114526
114527                 slidersEnter
114528                     .append('input')
114529                     .attr('class', function(d) { return 'display-option-input display-option-input-' + d; })
114530                     .attr('type', 'range')
114531                     .attr('min', _minVal)
114532                     .attr('max', _maxVal)
114533                     .attr('step', '0.05')
114534                     .on('input', function(d) {
114535                         var val = select(this).property('value');
114536                         updateValue(d, val);
114537                     });
114538
114539                 slidersEnter
114540                     .append('button')
114541                     .attr('title', _t('background.reset'))
114542                     .attr('class', function(d) { return 'display-option-reset display-option-reset-' + d; })
114543                     .on('click', function(d) {
114544                         if (event.button !== 0) { return; }
114545                         updateValue(d, 1);
114546                     })
114547                     .call(svgIcon('#iD-icon-' + (_mainLocalizer.textDirection() === 'rtl' ? 'redo' : 'undo')));
114548
114549                 // reset all button
114550                 containerEnter
114551                     .append('a')
114552                     .attr('class', 'display-option-resetlink')
114553                     .attr('href', '#')
114554                     .text(_t('background.reset_all'))
114555                     .on('click', function() {
114556                         for (var i = 0; i < _sliders.length; i++) {
114557                             updateValue(_sliders[i],1);
114558                         }
114559                     });
114560
114561                 // update
114562                 container = containerEnter
114563                     .merge(container);
114564
114565                 container.selectAll('.display-option-input')
114566                     .property('value', function(d) { return _options[d]; });
114567
114568                 container.selectAll('.display-option-value')
114569                     .text(function(d) { return Math.floor(_options[d] * 100) + '%'; });
114570
114571                 container.selectAll('.display-option-reset')
114572                     .classed('disabled', function(d) { return _options[d] === 1; });
114573
114574                 // first time only, set brightness if needed
114575                 if (containerEnter.size() && _options.brightness !== 1) {
114576                     context.background().brightness(_options.brightness);
114577                 }
114578             }
114579
114580             return section;
114581         }
114582
114583         function uiSettingsCustomBackground() {
114584             var dispatch$1 = dispatch('change');
114585
114586             function render(selection) {
114587                 // keep separate copies of original and current settings
114588                 var _origSettings = {
114589                     template: corePreferences('background-custom-template')
114590                 };
114591                 var _currSettings = {
114592                     template: corePreferences('background-custom-template')
114593                 };
114594
114595                 var example = 'https://{switch:a,b,c}.tile.openstreetmap.org/{zoom}/{x}/{y}.png';
114596                 var modal = uiConfirm(selection).okButton();
114597
114598                 modal
114599                     .classed('settings-modal settings-custom-background', true);
114600
114601                 modal.select('.modal-section.header')
114602                     .append('h3')
114603                     .text(_t('settings.custom_background.header'));
114604
114605
114606                 var textSection = modal.select('.modal-section.message-text');
114607
114608                 var instructions =
114609                     (_t('settings.custom_background.instructions.info')) + "\n" +
114610                     '\n' +
114611                     "#### " + (_t('settings.custom_background.instructions.wms.tokens_label')) + "\n" +
114612                     "* " + (_t('settings.custom_background.instructions.wms.tokens.proj')) + "\n" +
114613                     "* " + (_t('settings.custom_background.instructions.wms.tokens.wkid')) + "\n" +
114614                     "* " + (_t('settings.custom_background.instructions.wms.tokens.dimensions')) + "\n" +
114615                     "* " + (_t('settings.custom_background.instructions.wms.tokens.bbox')) + "\n" +
114616                     '\n' +
114617                     "#### " + (_t('settings.custom_background.instructions.tms.tokens_label')) + "\n" +
114618                     "* " + (_t('settings.custom_background.instructions.tms.tokens.xyz')) + "\n" +
114619                     "* " + (_t('settings.custom_background.instructions.tms.tokens.flipped_y')) + "\n" +
114620                     "* " + (_t('settings.custom_background.instructions.tms.tokens.switch')) + "\n" +
114621                     "* " + (_t('settings.custom_background.instructions.tms.tokens.quadtile')) + "\n" +
114622                     "* " + (_t('settings.custom_background.instructions.tms.tokens.scale_factor')) + "\n" +
114623                     '\n' +
114624                     "#### " + (_t('settings.custom_background.instructions.example')) + "\n" +
114625                     "`" + example + "`";
114626
114627                 textSection
114628                     .append('div')
114629                     .attr('class', 'instructions-template')
114630                     .html(marked_1(instructions));
114631
114632                 textSection
114633                     .append('textarea')
114634                     .attr('class', 'field-template')
114635                     .attr('placeholder', _t('settings.custom_background.template.placeholder'))
114636                     .call(utilNoAuto)
114637                     .property('value', _currSettings.template);
114638
114639
114640                 // insert a cancel button
114641                 var buttonSection = modal.select('.modal-section.buttons');
114642
114643                 buttonSection
114644                     .insert('button', '.ok-button')
114645                     .attr('class', 'button cancel-button secondary-action')
114646                     .text(_t('confirm.cancel'));
114647
114648
114649                 buttonSection.select('.cancel-button')
114650                     .on('click.cancel', clickCancel);
114651
114652                 buttonSection.select('.ok-button')
114653                     .attr('disabled', isSaveDisabled)
114654                     .on('click.save', clickSave);
114655
114656
114657                 function isSaveDisabled() {
114658                     return null;
114659                 }
114660
114661
114662                 // restore the original template
114663                 function clickCancel() {
114664                     textSection.select('.field-template').property('value', _origSettings.template);
114665                     corePreferences('background-custom-template', _origSettings.template);
114666                     this.blur();
114667                     modal.close();
114668                 }
114669
114670                 // accept the current template
114671                 function clickSave() {
114672                     _currSettings.template = textSection.select('.field-template').property('value');
114673                     corePreferences('background-custom-template', _currSettings.template);
114674                     this.blur();
114675                     modal.close();
114676                     dispatch$1.call('change', this, _currSettings);
114677                 }
114678             }
114679
114680             return utilRebind(render, dispatch$1, 'on');
114681         }
114682
114683         function uiSectionBackgroundList(context) {
114684
114685             var _backgroundList = select(null);
114686
114687             var _customSource = context.background().findSource('custom');
114688
114689             var _settingsCustomBackground = uiSettingsCustomBackground()
114690                 .on('change', customChanged);
114691
114692             var section = uiSection('background-list', context)
114693                 .title(_t('background.backgrounds'))
114694                 .disclosureContent(renderDisclosureContent);
114695
114696             function previousBackgroundID() {
114697                 return corePreferences('background-last-used-toggle');
114698             }
114699
114700             function renderDisclosureContent(selection) {
114701
114702                 // the background list
114703                 var container = selection.selectAll('.layer-background-list')
114704                     .data([0]);
114705
114706                 _backgroundList = container.enter()
114707                     .append('ul')
114708                     .attr('class', 'layer-list layer-background-list')
114709                     .attr('dir', 'auto')
114710                     .merge(container);
114711
114712
114713                 // add minimap toggle below list
114714                 var bgExtrasListEnter = selection.selectAll('.bg-extras-list')
114715                     .data([0])
114716                     .enter()
114717                     .append('ul')
114718                     .attr('class', 'layer-list bg-extras-list');
114719
114720                 var minimapLabelEnter = bgExtrasListEnter
114721                     .append('li')
114722                     .attr('class', 'minimap-toggle-item')
114723                     .append('label')
114724                     .call(uiTooltip()
114725                         .title(_t('background.minimap.tooltip'))
114726                         .keys([_t('background.minimap.key')])
114727                         .placement('top')
114728                     );
114729
114730                 minimapLabelEnter
114731                     .append('input')
114732                     .attr('type', 'checkbox')
114733                     .on('change', function() {
114734                         event.preventDefault();
114735                         uiMapInMap.toggle();
114736                     });
114737
114738                 minimapLabelEnter
114739                     .append('span')
114740                     .text(_t('background.minimap.description'));
114741
114742
114743                 var panelLabelEnter = bgExtrasListEnter
114744                     .append('li')
114745                     .attr('class', 'background-panel-toggle-item')
114746                     .append('label')
114747                     .call(uiTooltip()
114748                         .title(_t('background.panel.tooltip'))
114749                         .keys([uiCmd('⌘⇧' + _t('info_panels.background.key'))])
114750                         .placement('top')
114751                     );
114752
114753                 panelLabelEnter
114754                     .append('input')
114755                     .attr('type', 'checkbox')
114756                     .on('change', function() {
114757                         event.preventDefault();
114758                         context.ui().info.toggle('background');
114759                     });
114760
114761                 panelLabelEnter
114762                     .append('span')
114763                     .text(_t('background.panel.description'));
114764
114765                 var locPanelLabelEnter = bgExtrasListEnter
114766                     .append('li')
114767                     .attr('class', 'location-panel-toggle-item')
114768                     .append('label')
114769                     .call(uiTooltip()
114770                         .title(_t('background.location_panel.tooltip'))
114771                         .keys([uiCmd('⌘⇧' + _t('info_panels.location.key'))])
114772                         .placement('top')
114773                     );
114774
114775                 locPanelLabelEnter
114776                     .append('input')
114777                     .attr('type', 'checkbox')
114778                     .on('change', function() {
114779                         event.preventDefault();
114780                         context.ui().info.toggle('location');
114781                     });
114782
114783                 locPanelLabelEnter
114784                     .append('span')
114785                     .text(_t('background.location_panel.description'));
114786
114787
114788                 // "Info / Report a Problem" link
114789                 selection.selectAll('.imagery-faq')
114790                     .data([0])
114791                     .enter()
114792                     .append('div')
114793                     .attr('class', 'imagery-faq')
114794                     .append('a')
114795                     .attr('target', '_blank')
114796                     .call(svgIcon('#iD-icon-out-link', 'inline'))
114797                     .attr('href', 'https://github.com/openstreetmap/iD/blob/develop/FAQ.md#how-can-i-report-an-issue-with-background-imagery')
114798                     .append('span')
114799                     .text(_t('background.imagery_problem_faq'));
114800
114801                 _backgroundList
114802                     .call(drawListItems, 'radio', chooseBackground, function(d) { return !d.isHidden() && !d.overlay; });
114803             }
114804
114805             function setTooltips(selection) {
114806                 selection.each(function(d, i, nodes) {
114807                     var item = select(this).select('label');
114808                     var span = item.select('span');
114809                     var placement = (i < nodes.length / 2) ? 'bottom' : 'top';
114810                     var description = d.description();
114811                     var isOverflowing = (span.property('clientWidth') !== span.property('scrollWidth'));
114812
114813                     item.call(uiTooltip().destroyAny);
114814
114815                     if (d.id === previousBackgroundID()) {
114816                         item.call(uiTooltip()
114817                             .placement(placement)
114818                             .title('<div>' + _t('background.switch') + '</div>')
114819                             .keys([uiCmd('⌘' + _t('background.key'))])
114820                         );
114821                     } else if (description || isOverflowing) {
114822                         item.call(uiTooltip()
114823                             .placement(placement)
114824                             .title(description || d.name())
114825                         );
114826                     }
114827                 });
114828             }
114829
114830             function drawListItems(layerList, type, change, filter) {
114831                 var sources = context.background()
114832                     .sources(context.map().extent(), context.map().zoom(), true)
114833                     .filter(filter);
114834
114835                 var layerLinks = layerList.selectAll('li')
114836                     .data(sources, function(d) { return d.name(); });
114837
114838                 layerLinks.exit()
114839                     .remove();
114840
114841                 var enter = layerLinks.enter()
114842                     .append('li')
114843                     .classed('layer-custom', function(d) { return d.id === 'custom'; })
114844                     .classed('best', function(d) { return d.best(); });
114845
114846                 var label = enter
114847                     .append('label');
114848
114849                 label
114850                     .append('input')
114851                     .attr('type', type)
114852                     .attr('name', 'layers')
114853                     .on('change', change);
114854
114855                 label
114856                     .append('span')
114857                     .text(function(d) { return d.name(); });
114858
114859                 enter.filter(function(d) { return d.id === 'custom'; })
114860                     .append('button')
114861                     .attr('class', 'layer-browse')
114862                     .call(uiTooltip()
114863                         .title(_t('settings.custom_background.tooltip'))
114864                         .placement((_mainLocalizer.textDirection() === 'rtl') ? 'right' : 'left')
114865                     )
114866                     .on('click', editCustom)
114867                     .call(svgIcon('#iD-icon-more'));
114868
114869                 enter.filter(function(d) { return d.best(); })
114870                     .append('div')
114871                     .attr('class', 'best')
114872                     .call(uiTooltip()
114873                         .title(_t('background.best_imagery'))
114874                         .placement((_mainLocalizer.textDirection() === 'rtl') ? 'right' : 'left')
114875                     )
114876                     .append('span')
114877                     .html('&#9733;');
114878
114879
114880                 layerList.selectAll('li')
114881                     .sort(sortSources);
114882
114883                 layerList
114884                     .call(updateLayerSelections);
114885
114886
114887                 function sortSources(a, b) {
114888                     return a.best() && !b.best() ? -1
114889                         : b.best() && !a.best() ? 1
114890                         : d3_descending(a.area(), b.area()) || d3_ascending(a.name(), b.name()) || 0;
114891                 }
114892             }
114893
114894             function updateLayerSelections(selection) {
114895                 function active(d) {
114896                     return context.background().showsLayer(d);
114897                 }
114898
114899                 selection.selectAll('li')
114900                     .classed('active', active)
114901                     .classed('switch', function(d) { return d.id === previousBackgroundID(); })
114902                     .call(setTooltips)
114903                     .selectAll('input')
114904                     .property('checked', active);
114905             }
114906
114907
114908             function chooseBackground(d) {
114909                 if (d.id === 'custom' && !d.template()) {
114910                     return editCustom();
114911                 }
114912
114913                 event.preventDefault();
114914                 var previousBackground = context.background().baseLayerSource();
114915                 corePreferences('background-last-used-toggle', previousBackground.id);
114916                 corePreferences('background-last-used', d.id);
114917                 context.background().baseLayerSource(d);
114918                 document.activeElement.blur();
114919             }
114920
114921
114922             function customChanged(d) {
114923                 if (d && d.template) {
114924                     _customSource.template(d.template);
114925                     chooseBackground(_customSource);
114926                 } else {
114927                     _customSource.template('');
114928                     chooseBackground(context.background().findSource('none'));
114929                 }
114930             }
114931
114932
114933             function editCustom() {
114934                 event.preventDefault();
114935                 context.container()
114936                     .call(_settingsCustomBackground);
114937             }
114938
114939
114940             context.background()
114941                 .on('change.background_list', function() {
114942                     _backgroundList.call(updateLayerSelections);
114943                 });
114944
114945             context.map()
114946                 .on('move.background_list',
114947                     debounce(function() {
114948                         // layers in-view may have changed due to map move
114949                         window.requestIdleCallback(section.reRender);
114950                     }, 1000)
114951                 );
114952
114953             return section;
114954         }
114955
114956         function uiSectionBackgroundOffset(context) {
114957
114958             var section = uiSection('background-offset', context)
114959                 .title(_t('background.fix_misalignment'))
114960                 .disclosureContent(renderDisclosureContent)
114961                 .expandedByDefault(false);
114962
114963             var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
114964
114965             var _directions = [
114966                 ['right', [0.5, 0]],
114967                 ['top', [0, -0.5]],
114968                 ['left', [-0.5, 0]],
114969                 ['bottom', [0, 0.5]]
114970             ];
114971
114972
114973             function cancelEvent() {
114974                 event.stopPropagation();
114975                 event.preventDefault();
114976             }
114977
114978
114979             function updateValue() {
114980                 var meters = geoOffsetToMeters(context.background().offset());
114981                 var x = +meters[0].toFixed(2);
114982                 var y = +meters[1].toFixed(2);
114983
114984                 context.container().selectAll('.nudge-inner-rect')
114985                     .select('input')
114986                     .classed('error', false)
114987                     .property('value', x + ', ' + y);
114988
114989                 context.container().selectAll('.nudge-reset')
114990                     .classed('disabled', function() {
114991                         return (x === 0 && y === 0);
114992                     });
114993             }
114994
114995
114996             function resetOffset() {
114997                 context.background().offset([0, 0]);
114998                 updateValue();
114999             }
115000
115001
115002             function nudge(d) {
115003                 context.background().nudge(d, context.map().zoom());
115004                 updateValue();
115005             }
115006
115007
115008             function pointerdownNudgeButton(d) {
115009                 var interval;
115010                 var timeout = window.setTimeout(function() {
115011                         interval = window.setInterval(nudge.bind(null, d), 100);
115012                     }, 500);
115013
115014                 function doneNudge() {
115015                     window.clearTimeout(timeout);
115016                     window.clearInterval(interval);
115017                     select(window)
115018                         .on(_pointerPrefix + 'up.buttonoffset', null, true)
115019                         .on(_pointerPrefix + 'down.buttonoffset', null, true);
115020                 }
115021
115022                 select(window)
115023                     .on(_pointerPrefix + 'up.buttonoffset', doneNudge, true)
115024                     .on(_pointerPrefix + 'down.buttonoffset', doneNudge, true);
115025
115026                 nudge(d);
115027             }
115028
115029
115030             function inputOffset() {
115031                 var input = select(this);
115032                 var d = input.node().value;
115033
115034                 if (d === '') { return resetOffset(); }
115035
115036                 d = d.replace(/;/g, ',').split(',').map(function(n) {
115037                     // if n is NaN, it will always get mapped to false.
115038                     return !isNaN(n) && n;
115039                 });
115040
115041                 if (d.length !== 2 || !d[0] || !d[1]) {
115042                     input.classed('error', true);
115043                     return;
115044                 }
115045
115046                 context.background().offset(geoMetersToOffset(d));
115047                 updateValue();
115048             }
115049
115050
115051             function dragOffset() {
115052                 if (event.button !== 0) { return; }
115053
115054                 var origin = [event.clientX, event.clientY];
115055
115056                 var pointerId = event.pointerId || 'mouse';
115057
115058                 context.container()
115059                     .append('div')
115060                     .attr('class', 'nudge-surface');
115061
115062                 select(window)
115063                     .on(_pointerPrefix + 'move.drag-bg-offset', pointermove)
115064                     .on(_pointerPrefix + 'up.drag-bg-offset', pointerup);
115065
115066                 if (_pointerPrefix === 'pointer') {
115067                     select(window)
115068                         .on('pointercancel.drag-bg-offset', pointerup);
115069                 }
115070
115071                 function pointermove() {
115072                     if (pointerId !== (event.pointerId || 'mouse')) { return; }
115073
115074                     var latest = [event.clientX, event.clientY];
115075                     var d = [
115076                         -(origin[0] - latest[0]) / 4,
115077                         -(origin[1] - latest[1]) / 4
115078                     ];
115079
115080                     origin = latest;
115081                     nudge(d);
115082                 }
115083
115084                 function pointerup() {
115085                     if (pointerId !== (event.pointerId || 'mouse')) { return; }
115086                     if (event.button !== 0) { return; }
115087
115088                     context.container().selectAll('.nudge-surface')
115089                         .remove();
115090
115091                     select(window)
115092                         .on('.drag-bg-offset', null);
115093                 }
115094             }
115095
115096
115097             function renderDisclosureContent(selection) {
115098                 var container = selection.selectAll('.nudge-container')
115099                     .data([0]);
115100
115101                 var containerEnter = container.enter()
115102                     .append('div')
115103                     .attr('class', 'nudge-container cf');
115104
115105                 containerEnter
115106                     .append('div')
115107                     .attr('class', 'nudge-instructions')
115108                     .text(_t('background.offset'));
115109
115110                 var nudgeEnter = containerEnter
115111                     .append('div')
115112                     .attr('class', 'nudge-outer-rect')
115113                     .on(_pointerPrefix + 'down', dragOffset);
115114
115115                 nudgeEnter
115116                     .append('div')
115117                     .attr('class', 'nudge-inner-rect')
115118                     .append('input')
115119                     .on('change', inputOffset);
115120
115121                 containerEnter
115122                     .append('div')
115123                     .selectAll('button')
115124                     .data(_directions).enter()
115125                     .append('button')
115126                     .attr('class', function(d) { return d[0] + ' nudge'; })
115127                     .on('contextmenu', cancelEvent)
115128                     .on(_pointerPrefix + 'down', function(d) {
115129                         if (event.button !== 0) { return; }
115130                         pointerdownNudgeButton(d[1]);
115131                     });
115132
115133                 containerEnter
115134                     .append('button')
115135                     .attr('title', _t('background.reset'))
115136                     .attr('class', 'nudge-reset disabled')
115137                     .on('contextmenu', cancelEvent)
115138                     .on('click', function() {
115139                         event.preventDefault();
115140                         if (event.button !== 0) { return; }
115141                         resetOffset();
115142                     })
115143                     .call(svgIcon('#iD-icon-' + (_mainLocalizer.textDirection() === 'rtl' ? 'redo' : 'undo')));
115144
115145                 updateValue();
115146             }
115147
115148             context.background()
115149                 .on('change.backgroundOffset-update', updateValue);
115150
115151             return section;
115152         }
115153
115154         function uiSectionOverlayList(context) {
115155
115156             var section = uiSection('overlay-list', context)
115157                 .title(_t('background.overlays'))
115158                 .disclosureContent(renderDisclosureContent);
115159
115160             var _overlayList = select(null);
115161
115162             function setTooltips(selection) {
115163                 selection.each(function(d, i, nodes) {
115164                     var item = select(this).select('label');
115165                     var span = item.select('span');
115166                     var placement = (i < nodes.length / 2) ? 'bottom' : 'top';
115167                     var description = d.description();
115168                     var isOverflowing = (span.property('clientWidth') !== span.property('scrollWidth'));
115169
115170                     item.call(uiTooltip().destroyAny);
115171
115172                     if (description || isOverflowing) {
115173                         item.call(uiTooltip()
115174                             .placement(placement)
115175                             .title(description || d.name())
115176                         );
115177                     }
115178                 });
115179             }
115180
115181             function updateLayerSelections(selection) {
115182                 function active(d) {
115183                     return context.background().showsLayer(d);
115184                 }
115185
115186                 selection.selectAll('li')
115187                     .classed('active', active)
115188                     .call(setTooltips)
115189                     .selectAll('input')
115190                     .property('checked', active);
115191             }
115192
115193
115194             function chooseOverlay(d) {
115195                 event.preventDefault();
115196                 context.background().toggleOverlayLayer(d);
115197                 _overlayList.call(updateLayerSelections);
115198                 document.activeElement.blur();
115199             }
115200
115201             function drawListItems(layerList, type, change, filter) {
115202                 var sources = context.background()
115203                     .sources(context.map().extent(), context.map().zoom(), true)
115204                     .filter(filter);
115205
115206                 var layerLinks = layerList.selectAll('li')
115207                     .data(sources, function(d) { return d.name(); });
115208
115209                 layerLinks.exit()
115210                     .remove();
115211
115212                 var enter = layerLinks.enter()
115213                     .append('li');
115214
115215                 var label = enter
115216                     .append('label');
115217
115218                 label
115219                     .append('input')
115220                     .attr('type', type)
115221                     .attr('name', 'layers')
115222                     .on('change', change);
115223
115224                 label
115225                     .append('span')
115226                     .text(function(d) { return d.name(); });
115227
115228
115229                 layerList.selectAll('li')
115230                     .sort(sortSources);
115231
115232                 layerList
115233                     .call(updateLayerSelections);
115234
115235
115236                 function sortSources(a, b) {
115237                     return a.best() && !b.best() ? -1
115238                         : b.best() && !a.best() ? 1
115239                         : d3_descending(a.area(), b.area()) || d3_ascending(a.name(), b.name()) || 0;
115240                 }
115241             }
115242
115243             function renderDisclosureContent(selection) {
115244
115245                 var container = selection.selectAll('.layer-overlay-list')
115246                     .data([0]);
115247
115248                 _overlayList = container.enter()
115249                     .append('ul')
115250                     .attr('class', 'layer-list layer-overlay-list')
115251                     .attr('dir', 'auto')
115252                     .merge(container);
115253
115254                 _overlayList
115255                     .call(drawListItems, 'checkbox', chooseOverlay, function(d) { return !d.isHidden() && d.overlay; });
115256             }
115257
115258             context.map()
115259                 .on('move.overlay_list',
115260                     debounce(function() {
115261                         // layers in-view may have changed due to map move
115262                         window.requestIdleCallback(section.reRender);
115263                     }, 1000)
115264                 );
115265
115266             return section;
115267         }
115268
115269         function uiPaneBackground(context) {
115270
115271             var backgroundPane = uiPane('background', context)
115272                 .key(_t('background.key'))
115273                 .title(_t('background.title'))
115274                 .description(_t('background.description'))
115275                 .iconName('iD-icon-layers')
115276                 .sections([
115277                     uiSectionBackgroundList(context),
115278                     uiSectionOverlayList(context),
115279                     uiSectionBackgroundDisplayOptions(context),
115280                     uiSectionBackgroundOffset(context)
115281                 ]);
115282
115283             return backgroundPane;
115284         }
115285
115286         function uiPaneHelp(context) {
115287
115288             var docKeys = [
115289                 ['help', [
115290                     'welcome',
115291                     'open_data_h',
115292                     'open_data',
115293                     'before_start_h',
115294                     'before_start',
115295                     'open_source_h',
115296                     'open_source',
115297                     'open_source_help'
115298                 ]],
115299                 ['overview', [
115300                     'navigation_h',
115301                     'navigation_drag',
115302                     'navigation_zoom',
115303                     'features_h',
115304                     'features',
115305                     'nodes_ways'
115306                 ]],
115307                 ['editing', [
115308                     'select_h',
115309                     'select_left_click',
115310                     'select_right_click',
115311                     'select_space',
115312                     'multiselect_h',
115313                     'multiselect',
115314                     'multiselect_shift_click',
115315                     'multiselect_lasso',
115316                     'undo_redo_h',
115317                     'undo_redo',
115318                     'save_h',
115319                     'save',
115320                     'save_validation',
115321                     'upload_h',
115322                     'upload',
115323                     'backups_h',
115324                     'backups',
115325                     'keyboard_h',
115326                     'keyboard'
115327                 ]],
115328                 ['feature_editor', [
115329                     'intro',
115330                     'definitions',
115331                     'type_h',
115332                     'type',
115333                     'type_picker',
115334                     'fields_h',
115335                     'fields_all_fields',
115336                     'fields_example',
115337                     'fields_add_field',
115338                     'tags_h',
115339                     'tags_all_tags',
115340                     'tags_resources'
115341                 ]],
115342                 ['points', [
115343                     'intro',
115344                     'add_point_h',
115345                     'add_point',
115346                     'add_point_finish',
115347                     'move_point_h',
115348                     'move_point',
115349                     'delete_point_h',
115350                     'delete_point',
115351                     'delete_point_command'
115352                 ]],
115353                 ['lines', [
115354                     'intro',
115355                     'add_line_h',
115356                     'add_line',
115357                     'add_line_draw',
115358                     'add_line_continue',
115359                     'add_line_finish',
115360                     'modify_line_h',
115361                     'modify_line_dragnode',
115362                     'modify_line_addnode',
115363                     'connect_line_h',
115364                     'connect_line',
115365                     'connect_line_display',
115366                     'connect_line_drag',
115367                     'connect_line_tag',
115368                     'disconnect_line_h',
115369                     'disconnect_line_command',
115370                     'move_line_h',
115371                     'move_line_command',
115372                     'move_line_connected',
115373                     'delete_line_h',
115374                     'delete_line',
115375                     'delete_line_command'
115376                 ]],
115377                 ['areas', [
115378                     'intro',
115379                     'point_or_area_h',
115380                     'point_or_area',
115381                     'add_area_h',
115382                     'add_area_command',
115383                     'add_area_draw',
115384                     'add_area_continue',
115385                     'add_area_finish',
115386                     'square_area_h',
115387                     'square_area_command',
115388                     'modify_area_h',
115389                     'modify_area_dragnode',
115390                     'modify_area_addnode',
115391                     'delete_area_h',
115392                     'delete_area',
115393                     'delete_area_command'
115394                 ]],
115395                 ['relations', [
115396                     'intro',
115397                     'edit_relation_h',
115398                     'edit_relation',
115399                     'edit_relation_add',
115400                     'edit_relation_delete',
115401                     'maintain_relation_h',
115402                     'maintain_relation',
115403                     'relation_types_h',
115404                     'multipolygon_h',
115405                     'multipolygon',
115406                     'multipolygon_create',
115407                     'multipolygon_merge',
115408                     'turn_restriction_h',
115409                     'turn_restriction',
115410                     'turn_restriction_field',
115411                     'turn_restriction_editing',
115412                     'route_h',
115413                     'route',
115414                     'route_add',
115415                     'boundary_h',
115416                     'boundary',
115417                     'boundary_add'
115418                 ]],
115419                 ['notes', [
115420                     'intro',
115421                     'add_note_h',
115422                     'add_note',
115423                     'place_note',
115424                     'move_note',
115425                     'update_note_h',
115426                     'update_note',
115427                     'save_note_h',
115428                     'save_note'
115429                 ]],
115430                 ['imagery', [
115431                     'intro',
115432                     'sources_h',
115433                     'choosing',
115434                     'sources',
115435                     'offsets_h',
115436                     'offset',
115437                     'offset_change'
115438                 ]],
115439                 ['streetlevel', [
115440                     'intro',
115441                     'using_h',
115442                     'using',
115443                     'photos',
115444                     'viewer'
115445                 ]],
115446                 ['gps', [
115447                     'intro',
115448                     'survey',
115449                     'using_h',
115450                     'using',
115451                     'tracing',
115452                     'upload'
115453                 ]],
115454                 ['qa', [
115455                     'intro',
115456                     'tools_h',
115457                     'tools',
115458                     'issues_h',
115459                     'issues'
115460                 ]]
115461             ];
115462
115463             var headings = {
115464                 'help.help.open_data_h': 3,
115465                 'help.help.before_start_h': 3,
115466                 'help.help.open_source_h': 3,
115467                 'help.overview.navigation_h': 3,
115468                 'help.overview.features_h': 3,
115469                 'help.editing.select_h': 3,
115470                 'help.editing.multiselect_h': 3,
115471                 'help.editing.undo_redo_h': 3,
115472                 'help.editing.save_h': 3,
115473                 'help.editing.upload_h': 3,
115474                 'help.editing.backups_h': 3,
115475                 'help.editing.keyboard_h': 3,
115476                 'help.feature_editor.type_h': 3,
115477                 'help.feature_editor.fields_h': 3,
115478                 'help.feature_editor.tags_h': 3,
115479                 'help.points.add_point_h': 3,
115480                 'help.points.move_point_h': 3,
115481                 'help.points.delete_point_h': 3,
115482                 'help.lines.add_line_h': 3,
115483                 'help.lines.modify_line_h': 3,
115484                 'help.lines.connect_line_h': 3,
115485                 'help.lines.disconnect_line_h': 3,
115486                 'help.lines.move_line_h': 3,
115487                 'help.lines.delete_line_h': 3,
115488                 'help.areas.point_or_area_h': 3,
115489                 'help.areas.add_area_h': 3,
115490                 'help.areas.square_area_h': 3,
115491                 'help.areas.modify_area_h': 3,
115492                 'help.areas.delete_area_h': 3,
115493                 'help.relations.edit_relation_h': 3,
115494                 'help.relations.maintain_relation_h': 3,
115495                 'help.relations.relation_types_h': 2,
115496                 'help.relations.multipolygon_h': 3,
115497                 'help.relations.turn_restriction_h': 3,
115498                 'help.relations.route_h': 3,
115499                 'help.relations.boundary_h': 3,
115500                 'help.notes.add_note_h': 3,
115501                 'help.notes.update_note_h': 3,
115502                 'help.notes.save_note_h': 3,
115503                 'help.imagery.sources_h': 3,
115504                 'help.imagery.offsets_h': 3,
115505                 'help.streetlevel.using_h': 3,
115506                 'help.gps.using_h': 3,
115507                 'help.qa.tools_h': 3,
115508                 'help.qa.issues_h': 3
115509             };
115510
115511             // For each section, squash all the texts into a single markdown document
115512             var docs = docKeys.map(function(key) {
115513                 var helpkey = 'help.' + key[0];
115514                 var helpPaneReplacements = { version: context.version };
115515                 var text = key[1].reduce(function(all, part) {
115516                     var subkey = helpkey + '.' + part;
115517                     var depth = headings[subkey];                              // is this subkey a heading?
115518                     var hhh = depth ? Array(depth + 1).join('#') + ' ' : '';   // if so, prepend with some ##'s
115519                     return all + hhh + helpString(subkey, helpPaneReplacements) + '\n\n';
115520                 }, '');
115521
115522                 return {
115523                     title: _t(helpkey + '.title'),
115524                     html: marked_1(text.trim())
115525                         // use keyboard key styling for shortcuts
115526                         .replace(/<code>/g, '<kbd>')
115527                         .replace(/<\/code>/g, '<\/kbd>')
115528                 };
115529             });
115530
115531             var helpPane = uiPane('help', context)
115532                 .key(_t('help.key'))
115533                 .title(_t('help.title'))
115534                 .description(_t('help.title'))
115535                 .iconName('iD-icon-help');
115536
115537             helpPane.renderContent = function(content) {
115538
115539                 function clickHelp(d, i) {
115540                     var rtl = (_mainLocalizer.textDirection() === 'rtl');
115541                     content.property('scrollTop', 0);
115542                     helpPane.selection().select('.pane-heading h2').html(d.title);
115543
115544                     body.html(d.html);
115545                     body.selectAll('a')
115546                         .attr('target', '_blank');
115547                     menuItems.classed('selected', function(m) {
115548                         return m.title === d.title;
115549                     });
115550
115551                     nav.html('');
115552                     if (rtl) {
115553                         nav.call(drawNext).call(drawPrevious);
115554                     } else {
115555                         nav.call(drawPrevious).call(drawNext);
115556                     }
115557
115558
115559                     function drawNext(selection) {
115560                         if (i < docs.length - 1) {
115561                             var nextLink = selection
115562                                 .append('a')
115563                                 .attr('class', 'next')
115564                                 .on('click', function() {
115565                                     clickHelp(docs[i + 1], i + 1);
115566                                 });
115567
115568                             nextLink
115569                                 .append('span')
115570                                 .text(docs[i + 1].title)
115571                                 .call(svgIcon((rtl ? '#iD-icon-backward' : '#iD-icon-forward'), 'inline'));
115572                         }
115573                     }
115574
115575
115576                     function drawPrevious(selection) {
115577                         if (i > 0) {
115578                             var prevLink = selection
115579                                 .append('a')
115580                                 .attr('class', 'previous')
115581                                 .on('click', function() {
115582                                     clickHelp(docs[i - 1], i - 1);
115583                                 });
115584
115585                             prevLink
115586                                 .call(svgIcon((rtl ? '#iD-icon-forward' : '#iD-icon-backward'), 'inline'))
115587                                 .append('span')
115588                                 .text(docs[i - 1].title);
115589                         }
115590                     }
115591                 }
115592
115593
115594                 function clickWalkthrough() {
115595                     if (context.inIntro()) { return; }
115596                     context.container().call(uiIntro(context));
115597                     context.ui().togglePanes();
115598                 }
115599
115600
115601                 function clickShortcuts() {
115602                     context.container().call(uiShortcuts(context), true);
115603                 }
115604
115605                 var toc = content
115606                     .append('ul')
115607                     .attr('class', 'toc');
115608
115609                 var menuItems = toc.selectAll('li')
115610                     .data(docs)
115611                     .enter()
115612                     .append('li')
115613                     .append('a')
115614                     .html(function(d) { return d.title; })
115615                     .on('click', clickHelp);
115616
115617                 var shortcuts = toc
115618                     .append('li')
115619                     .attr('class', 'shortcuts')
115620                     .call(uiTooltip()
115621                         .title(_t('shortcuts.tooltip'))
115622                         .keys(['?'])
115623                         .placement('top')
115624                     )
115625                     .append('a')
115626                     .on('click', clickShortcuts);
115627
115628                 shortcuts
115629                     .append('div')
115630                     .text(_t('shortcuts.title'));
115631
115632                 var walkthrough = toc
115633                     .append('li')
115634                     .attr('class', 'walkthrough')
115635                     .append('a')
115636                     .on('click', clickWalkthrough);
115637
115638                 walkthrough
115639                     .append('svg')
115640                     .attr('class', 'logo logo-walkthrough')
115641                     .append('use')
115642                     .attr('xlink:href', '#iD-logo-walkthrough');
115643
115644                 walkthrough
115645                     .append('div')
115646                     .text(_t('splash.walkthrough'));
115647
115648
115649                 var helpContent = content
115650                     .append('div')
115651                     .attr('class', 'left-content');
115652
115653                 var body = helpContent
115654                     .append('div')
115655                     .attr('class', 'body');
115656
115657                 var nav = helpContent
115658                     .append('div')
115659                     .attr('class', 'nav');
115660
115661                 clickHelp(docs[0], 0);
115662
115663             };
115664
115665             return helpPane;
115666         }
115667
115668         function uiSectionValidationIssues(id, severity, context) {
115669
115670             var _issues = [];
115671
115672             var section = uiSection(id, context)
115673                 .title(function() {
115674                     if (!_issues) { return ''; }
115675                     var issueCountText = _issues.length > 1000 ? '1000+' : String(_issues.length);
115676                     return _t('issues.' + severity + 's.list_title', { count: issueCountText });
115677                 })
115678                 .disclosureContent(renderDisclosureContent)
115679                 .shouldDisplay(function() {
115680                     return _issues && _issues.length;
115681                 });
115682
115683             function getOptions() {
115684                 return {
115685                     what: corePreferences('validate-what') || 'edited',
115686                     where: corePreferences('validate-where') || 'all'
115687                 };
115688             }
115689
115690             // get and cache the issues to display, unordered
115691             function reloadIssues() {
115692                 _issues = context.validator().getIssuesBySeverity(getOptions())[severity];
115693             }
115694
115695             function renderDisclosureContent(selection) {
115696
115697                 var center = context.map().center();
115698                 var graph = context.graph();
115699
115700                 // sort issues by distance away from the center of the map
115701                 var issues = _issues.map(function withDistance(issue) {
115702                         var extent = issue.extent(graph);
115703                         var dist = extent ? geoSphericalDistance(center, extent.center()) : 0;
115704                         return Object.assign(issue, { dist: dist });
115705                     })
115706                     .sort(function byDistance(a, b) {
115707                         return a.dist - b.dist;
115708                     });
115709
115710                 // cut off at 1000
115711                 issues = issues.slice(0, 1000);
115712
115713                 //renderIgnoredIssuesReset(_warningsSelection);
115714
115715                 selection
115716                     .call(drawIssuesList, issues);
115717             }
115718
115719             function drawIssuesList(selection, issues) {
115720                 var list = selection.selectAll('.issues-list')
115721                     .data([0]);
115722
115723                 list = list.enter()
115724                     .append('ul')
115725                     .attr('class', 'layer-list issues-list ' + severity + 's-list')
115726                     .merge(list);
115727
115728
115729                 var items = list.selectAll('li')
115730                     .data(issues, function(d) { return d.id; });
115731
115732                 // Exit
115733                 items.exit()
115734                     .remove();
115735
115736                 // Enter
115737                 var itemsEnter = items.enter()
115738                     .append('li')
115739                     .attr('class', function (d) { return 'issue severity-' + d.severity; })
115740                     .on('click', function(d) {
115741                         context.validator().focusIssue(d);
115742                     })
115743                     .on('mouseover', function(d) {
115744                         utilHighlightEntities(d.entityIds, true, context);
115745                     })
115746                     .on('mouseout', function(d) {
115747                         utilHighlightEntities(d.entityIds, false, context);
115748                     });
115749
115750
115751                 var labelsEnter = itemsEnter
115752                     .append('div')
115753                     .attr('class', 'issue-label');
115754
115755                 var textEnter = labelsEnter
115756                     .append('span')
115757                     .attr('class', 'issue-text');
115758
115759                 textEnter
115760                     .append('span')
115761                     .attr('class', 'issue-icon')
115762                     .each(function(d) {
115763                         var iconName = '#iD-icon-' + (d.severity === 'warning' ? 'alert' : 'error');
115764                         select(this)
115765                             .call(svgIcon(iconName));
115766                     });
115767
115768                 textEnter
115769                     .append('span')
115770                     .attr('class', 'issue-message');
115771
115772                 /*
115773                 labelsEnter
115774                     .append('span')
115775                     .attr('class', 'issue-autofix')
115776                     .each(function(d) {
115777                         if (!d.autoFix) return;
115778
115779                         d3_select(this)
115780                             .append('button')
115781                             .attr('title', t('issues.fix_one.title'))
115782                             .datum(d.autoFix)  // set button datum to the autofix
115783                             .attr('class', 'autofix action')
115784                             .on('click', function(d) {
115785                                 d3_event.preventDefault();
115786                                 d3_event.stopPropagation();
115787
115788                                 var issuesEntityIDs = d.issue.entityIds;
115789                                 utilHighlightEntities(issuesEntityIDs.concat(d.entityIds), false, context);
115790
115791                                 context.perform.apply(context, d.autoArgs);
115792                                 context.validator().validate();
115793                             })
115794                             .call(svgIcon('#iD-icon-wrench'));
115795                     });
115796                 */
115797
115798                 // Update
115799                 items = items
115800                     .merge(itemsEnter)
115801                     .order();
115802
115803                 items.selectAll('.issue-message')
115804                     .text(function(d) {
115805                         return d.message(context);
115806                     });
115807
115808                 /*
115809                 // autofix
115810                 var canAutoFix = issues.filter(function(issue) { return issue.autoFix; });
115811
115812                 var autoFixAll = selection.selectAll('.autofix-all')
115813                     .data(canAutoFix.length ? [0] : []);
115814
115815                 // exit
115816                 autoFixAll.exit()
115817                     .remove();
115818
115819                 // enter
115820                 var autoFixAllEnter = autoFixAll.enter()
115821                     .insert('div', '.issues-list')
115822                     .attr('class', 'autofix-all');
115823
115824                 var linkEnter = autoFixAllEnter
115825                     .append('a')
115826                     .attr('class', 'autofix-all-link')
115827                     .attr('href', '#');
115828
115829                 linkEnter
115830                     .append('span')
115831                     .attr('class', 'autofix-all-link-text')
115832                     .text(t('issues.fix_all.title'));
115833
115834                 linkEnter
115835                     .append('span')
115836                     .attr('class', 'autofix-all-link-icon')
115837                     .call(svgIcon('#iD-icon-wrench'));
115838
115839                 if (severity === 'warning') {
115840                     renderIgnoredIssuesReset(selection);
115841                 }
115842
115843                 // update
115844                 autoFixAll = autoFixAll
115845                     .merge(autoFixAllEnter);
115846
115847                 autoFixAll.selectAll('.autofix-all-link')
115848                     .on('click', function() {
115849                         context.pauseChangeDispatch();
115850                         context.perform(actionNoop());
115851                         canAutoFix.forEach(function(issue) {
115852                             var args = issue.autoFix.autoArgs.slice();  // copy
115853                             if (typeof args[args.length - 1] !== 'function') {
115854                                 args.pop();
115855                             }
115856                             args.push(t('issues.fix_all.annotation'));
115857                             context.replace.apply(context, args);
115858                         });
115859                         context.resumeChangeDispatch();
115860                         context.validator().validate();
115861                     });
115862                 */
115863             }
115864
115865             context.validator().on('validated.uiSectionValidationIssues' + id, function() {
115866                 window.requestIdleCallback(function() {
115867                     reloadIssues();
115868                     section.reRender();
115869                 });
115870             });
115871
115872             context.map().on('move.uiSectionValidationIssues' + id,
115873                 debounce(function() {
115874                     window.requestIdleCallback(function() {
115875                         if (getOptions().where === 'visible') {
115876                             // must refetch issues if they are viewport-dependent
115877                             reloadIssues();
115878                         }
115879                         // always reload list to re-sort-by-distance
115880                         section.reRender();
115881                     });
115882                 }, 1000)
115883             );
115884
115885             return section;
115886         }
115887
115888         function uiSectionValidationOptions(context) {
115889
115890             var section = uiSection('issues-options', context)
115891                 .content(renderContent);
115892
115893             function renderContent(selection) {
115894
115895                 var container = selection.selectAll('.issues-options-container')
115896                     .data([0]);
115897
115898                 container = container.enter()
115899                     .append('div')
115900                     .attr('class', 'issues-options-container')
115901                     .merge(container);
115902
115903                 var data = [
115904                     { key: 'what', values: ['edited', 'all'] },
115905                     { key: 'where', values: ['visible', 'all'] }
115906                 ];
115907
115908                 var options = container.selectAll('.issues-option')
115909                     .data(data, function(d) { return d.key; });
115910
115911                 var optionsEnter = options.enter()
115912                     .append('div')
115913                     .attr('class', function(d) { return 'issues-option issues-option-' + d.key; });
115914
115915                 optionsEnter
115916                     .append('div')
115917                     .attr('class', 'issues-option-title')
115918                     .text(function(d) { return _t('issues.options.' + d.key + '.title'); });
115919
115920                 var valuesEnter = optionsEnter.selectAll('label')
115921                     .data(function(d) {
115922                         return d.values.map(function(val) { return { value: val, key: d.key }; });
115923                     })
115924                     .enter()
115925                     .append('label');
115926
115927                 valuesEnter
115928                     .append('input')
115929                     .attr('type', 'radio')
115930                     .attr('name', function(d) { return 'issues-option-' + d.key; })
115931                     .attr('value', function(d) { return d.value; })
115932                     .property('checked', function(d) { return getOptions()[d.key] === d.value; })
115933                     .on('change', function(d) { updateOptionValue(d.key, d.value); });
115934
115935                 valuesEnter
115936                     .append('span')
115937                     .text(function(d) { return _t('issues.options.' + d.key + '.' + d.value); });
115938             }
115939
115940             function getOptions() {
115941                 return {
115942                     what: corePreferences('validate-what') || 'edited',  // 'all', 'edited'
115943                     where: corePreferences('validate-where') || 'all'    // 'all', 'visible'
115944                 };
115945             }
115946
115947             function updateOptionValue(d, val) {
115948                 if (!val && event && event.target) {
115949                     val = event.target.value;
115950                 }
115951
115952                 corePreferences('validate-' + d, val);
115953                 context.validator().validate();
115954             }
115955
115956             return section;
115957         }
115958
115959         function uiSectionValidationRules(context) {
115960
115961             var MINSQUARE = 0;
115962             var MAXSQUARE = 20;
115963             var DEFAULTSQUARE = 5;  // see also unsquare_way.js
115964
115965             var section = uiSection('issues-rules', context)
115966                 .disclosureContent(renderDisclosureContent)
115967                 .title(_t('issues.rules.title'));
115968
115969             var _ruleKeys = context.validator().getRuleKeys()
115970                 .filter(function(key) { return key !== 'maprules'; })
115971                 .sort(function(key1, key2) {
115972                     // alphabetize by localized title
115973                     return _t('issues.' + key1 + '.title') < _t('issues.' + key2 + '.title') ? -1 : 1;
115974                 });
115975
115976             function renderDisclosureContent(selection) {
115977                 var container = selection.selectAll('.issues-rulelist-container')
115978                     .data([0]);
115979
115980                 var containerEnter = container.enter()
115981                     .append('div')
115982                     .attr('class', 'issues-rulelist-container');
115983
115984                 containerEnter
115985                     .append('ul')
115986                     .attr('class', 'layer-list issue-rules-list');
115987
115988                 var ruleLinks = containerEnter
115989                     .append('div')
115990                     .attr('class', 'issue-rules-links section-footer');
115991
115992                 ruleLinks
115993                     .append('a')
115994                     .attr('class', 'issue-rules-link')
115995                     .attr('href', '#')
115996                     .text(_t('issues.enable_all'))
115997                     .on('click', function() {
115998                         context.validator().disableRules([]);
115999                     });
116000
116001                 ruleLinks
116002                     .append('a')
116003                     .attr('class', 'issue-rules-link')
116004                     .attr('href', '#')
116005                     .text(_t('issues.disable_all'))
116006                     .on('click', function() {
116007                         context.validator().disableRules(_ruleKeys);
116008                     });
116009
116010
116011                 // Update
116012                 container = container
116013                     .merge(containerEnter);
116014
116015                 container.selectAll('.issue-rules-list')
116016                     .call(drawListItems, _ruleKeys, 'checkbox', 'rule', toggleRule, isRuleEnabled);
116017             }
116018
116019             function drawListItems(selection, data, type, name, change, active) {
116020                 var items = selection.selectAll('li')
116021                     .data(data);
116022
116023                 // Exit
116024                 items.exit()
116025                     .remove();
116026
116027                 // Enter
116028                 var enter = items.enter()
116029                     .append('li');
116030
116031                 if (name === 'rule') {
116032                     enter
116033                         .call(uiTooltip()
116034                             .title(function(d) { return _t('issues.' + d + '.tip'); })
116035                             .placement('top')
116036                         );
116037                 }
116038
116039                 var label = enter
116040                     .append('label');
116041
116042                 label
116043                     .append('input')
116044                     .attr('type', type)
116045                     .attr('name', name)
116046                     .on('change', change);
116047
116048                 label
116049                     .append('span')
116050                     .html(function(d) {
116051                         var params = {};
116052                         if (d === 'unsquare_way') {
116053                             params.val = '<span class="square-degrees"></span>';
116054                         }
116055                         return _t('issues.' + d + '.title', params);
116056                     });
116057
116058                 // Update
116059                 items = items
116060                     .merge(enter);
116061
116062                 items
116063                     .classed('active', active)
116064                     .selectAll('input')
116065                     .property('checked', active)
116066                     .property('indeterminate', false);
116067
116068
116069                 // user-configurable square threshold
116070                 var degStr = corePreferences('validate-square-degrees');
116071                 if (degStr === null) {
116072                     degStr = '' + DEFAULTSQUARE;
116073                 }
116074
116075                 var span = items.selectAll('.square-degrees');
116076                 var input = span.selectAll('.square-degrees-input')
116077                     .data([0]);
116078
116079                 // enter / update
116080                 input.enter()
116081                     .append('input')
116082                     .attr('type', 'number')
116083                     .attr('min', '' + MINSQUARE)
116084                     .attr('max', '' + MAXSQUARE)
116085                     .attr('step', '0.5')
116086                     .attr('class', 'square-degrees-input')
116087                     .call(utilNoAuto)
116088                     .on('click', function () {
116089                         event.preventDefault();
116090                         event.stopPropagation();
116091                         this.select();
116092                     })
116093                     .on('keyup', function () {
116094                         if (event.keyCode === 13) { // enter
116095                             this.blur();
116096                             this.select();
116097                         }
116098                     })
116099                     .on('blur', changeSquare)
116100                     .merge(input)
116101                     .property('value', degStr);
116102             }
116103
116104             function changeSquare() {
116105                 var input = select(this);
116106                 var degStr = utilGetSetValue(input).trim();
116107                 var degNum = parseFloat(degStr, 10);
116108
116109                 if (!isFinite(degNum)) {
116110                     degNum = DEFAULTSQUARE;
116111                 } else if (degNum > MAXSQUARE) {
116112                     degNum = MAXSQUARE;
116113                 } else if (degNum < MINSQUARE) {
116114                     degNum = MINSQUARE;
116115                 }
116116
116117                 degNum = Math.round(degNum * 10 ) / 10;   // round to 1 decimal
116118                 degStr = '' + degNum;
116119
116120                 input
116121                     .property('value', degStr);
116122
116123                 corePreferences('validate-square-degrees', degStr);
116124                 context.validator().reloadUnsquareIssues();
116125             }
116126
116127             function isRuleEnabled(d) {
116128                 return context.validator().isRuleEnabled(d);
116129             }
116130
116131             function toggleRule(d) {
116132                 context.validator().toggleRule(d);
116133             }
116134
116135             context.validator().on('validated.uiSectionValidationRules', function() {
116136                 window.requestIdleCallback(section.reRender);
116137             });
116138
116139             return section;
116140         }
116141
116142         function uiSectionValidationStatus(context) {
116143
116144             var section = uiSection('issues-status', context)
116145                 .content(renderContent)
116146                 .shouldDisplay(function() {
116147                     var issues = context.validator().getIssues(getOptions());
116148                     return issues.length === 0;
116149                 });
116150
116151             function getOptions() {
116152                 return {
116153                     what: corePreferences('validate-what') || 'edited',
116154                     where: corePreferences('validate-where') || 'all'
116155                 };
116156             }
116157
116158             function renderContent(selection) {
116159
116160                 var box = selection.selectAll('.box')
116161                     .data([0]);
116162
116163                 var boxEnter = box.enter()
116164                     .append('div')
116165                     .attr('class', 'box');
116166
116167                 boxEnter
116168                     .append('div')
116169                     .call(svgIcon('#iD-icon-apply', 'pre-text'));
116170
116171                 var noIssuesMessage = boxEnter
116172                     .append('span');
116173
116174                 noIssuesMessage
116175                     .append('strong')
116176                     .attr('class', 'message');
116177
116178                 noIssuesMessage
116179                     .append('br');
116180
116181                 noIssuesMessage
116182                     .append('span')
116183                     .attr('class', 'details');
116184
116185                 renderIgnoredIssuesReset(selection);
116186                 setNoIssuesText(selection);
116187             }
116188
116189             function renderIgnoredIssuesReset(selection) {
116190
116191                 var ignoredIssues = context.validator()
116192                     .getIssues({ what: 'all', where: 'all', includeDisabledRules: true, includeIgnored: 'only' });
116193
116194                 var resetIgnored = selection.selectAll('.reset-ignored')
116195                     .data(ignoredIssues.length ? [0] : []);
116196
116197                 // exit
116198                 resetIgnored.exit()
116199                     .remove();
116200
116201                 // enter
116202                 var resetIgnoredEnter = resetIgnored.enter()
116203                     .append('div')
116204                     .attr('class', 'reset-ignored section-footer');
116205
116206                 resetIgnoredEnter
116207                     .append('a')
116208                     .attr('href', '#');
116209
116210                 // update
116211                 resetIgnored = resetIgnored
116212                     .merge(resetIgnoredEnter);
116213
116214                 resetIgnored.select('a')
116215                     .text(_t('issues.reset_ignored', { count: ignoredIssues.length.toString() }));
116216
116217                 resetIgnored.on('click', function() {
116218                     context.validator().resetIgnoredIssues();
116219                 });
116220             }
116221
116222             function setNoIssuesText(selection) {
116223
116224                 var opts = getOptions();
116225
116226                 function checkForHiddenIssues(cases) {
116227                     for (var type in cases) {
116228                         var hiddenOpts = cases[type];
116229                         var hiddenIssues = context.validator().getIssues(hiddenOpts);
116230                         if (hiddenIssues.length) {
116231                             selection.select('.box .details')
116232                                 .text(_t(
116233                                     'issues.no_issues.hidden_issues.' + type,
116234                                     { count: hiddenIssues.length.toString() }
116235                                 ));
116236                             return;
116237                         }
116238                     }
116239                     selection.select('.box .details')
116240                         .text(_t('issues.no_issues.hidden_issues.none'));
116241                 }
116242
116243                 var messageType;
116244
116245                 if (opts.what === 'edited' && opts.where === 'visible') {
116246
116247                     messageType = 'edits_in_view';
116248
116249                     checkForHiddenIssues({
116250                         elsewhere: { what: 'edited', where: 'all' },
116251                         everything_else: { what: 'all', where: 'visible' },
116252                         disabled_rules: { what: 'edited', where: 'visible', includeDisabledRules: 'only' },
116253                         everything_else_elsewhere: { what: 'all', where: 'all' },
116254                         disabled_rules_elsewhere: { what: 'edited', where: 'all', includeDisabledRules: 'only' },
116255                         ignored_issues: { what: 'edited', where: 'visible', includeIgnored: 'only' },
116256                         ignored_issues_elsewhere: { what: 'edited', where: 'all', includeIgnored: 'only' }
116257                     });
116258
116259                 } else if (opts.what === 'edited' && opts.where === 'all') {
116260
116261                     messageType = 'edits';
116262
116263                     checkForHiddenIssues({
116264                         everything_else: { what: 'all', where: 'all' },
116265                         disabled_rules: { what: 'edited', where: 'all', includeDisabledRules: 'only' },
116266                         ignored_issues: { what: 'edited', where: 'all', includeIgnored: 'only' }
116267                     });
116268
116269                 } else if (opts.what === 'all' && opts.where === 'visible') {
116270
116271                     messageType = 'everything_in_view';
116272
116273                     checkForHiddenIssues({
116274                         elsewhere: { what: 'all', where: 'all' },
116275                         disabled_rules: { what: 'all', where: 'visible', includeDisabledRules: 'only' },
116276                         disabled_rules_elsewhere: { what: 'all', where: 'all', includeDisabledRules: 'only' },
116277                         ignored_issues: { what: 'all', where: 'visible', includeIgnored: 'only' },
116278                         ignored_issues_elsewhere: { what: 'all', where: 'all', includeIgnored: 'only' }
116279                     });
116280                 } else if (opts.what === 'all' && opts.where === 'all') {
116281
116282                     messageType = 'everything';
116283
116284                     checkForHiddenIssues({
116285                         disabled_rules: { what: 'all', where: 'all', includeDisabledRules: 'only' },
116286                         ignored_issues: { what: 'all', where: 'all', includeIgnored: 'only' }
116287                     });
116288                 }
116289
116290                 if (opts.what === 'edited' && context.history().difference().summary().length === 0) {
116291                     messageType = 'no_edits';
116292                 }
116293
116294                 selection.select('.box .message')
116295                     .text(_t('issues.no_issues.message.' + messageType));
116296
116297             }
116298
116299             context.validator().on('validated.uiSectionValidationStatus', function() {
116300                 window.requestIdleCallback(section.reRender);
116301             });
116302
116303             context.map().on('move.uiSectionValidationStatus',
116304                 debounce(function() {
116305                     window.requestIdleCallback(section.reRender);
116306                 }, 1000)
116307             );
116308
116309             return section;
116310         }
116311
116312         function uiPaneIssues(context) {
116313
116314             var issuesPane = uiPane('issues', context)
116315                 .key(_t('issues.key'))
116316                 .title(_t('issues.title'))
116317                 .description(_t('issues.title'))
116318                 .iconName('iD-icon-alert')
116319                 .sections([
116320                     uiSectionValidationOptions(context),
116321                     uiSectionValidationStatus(context),
116322                     uiSectionValidationIssues('issues-errors', 'error', context),
116323                     uiSectionValidationIssues('issues-warnings', 'warning', context),
116324                     uiSectionValidationRules(context)
116325                 ]);
116326
116327             return issuesPane;
116328         }
116329
116330         function uiSettingsCustomData(context) {
116331             var dispatch$1 = dispatch('change');
116332
116333             function render(selection) {
116334                 var dataLayer = context.layers().layer('data');
116335
116336                 // keep separate copies of original and current settings
116337                 var _origSettings = {
116338                     fileList: (dataLayer && dataLayer.fileList()) || null,
116339                     url: corePreferences('settings-custom-data-url')
116340                 };
116341                 var _currSettings = {
116342                     fileList: (dataLayer && dataLayer.fileList()) || null,
116343                     url: corePreferences('settings-custom-data-url')
116344                 };
116345
116346                 // var example = 'https://{switch:a,b,c}.tile.openstreetmap.org/{zoom}/{x}/{y}.png';
116347                 var modal = uiConfirm(selection).okButton();
116348
116349                 modal
116350                     .classed('settings-modal settings-custom-data', true);
116351
116352                 modal.select('.modal-section.header')
116353                     .append('h3')
116354                     .text(_t('settings.custom_data.header'));
116355
116356
116357                 var textSection = modal.select('.modal-section.message-text');
116358
116359                 textSection
116360                     .append('pre')
116361                     .attr('class', 'instructions-file')
116362                     .text(_t('settings.custom_data.file.instructions'));
116363
116364                 textSection
116365                     .append('input')
116366                     .attr('class', 'field-file')
116367                     .attr('type', 'file')
116368                     .property('files', _currSettings.fileList)  // works for all except IE11
116369                     .on('change', function() {
116370                         var files = event.target.files;
116371                         if (files && files.length) {
116372                             _currSettings.url = '';
116373                             textSection.select('.field-url').property('value', '');
116374                             _currSettings.fileList = files;
116375                         } else {
116376                             _currSettings.fileList = null;
116377                         }
116378                     });
116379
116380                 textSection
116381                     .append('h4')
116382                     .text(_t('settings.custom_data.or'));
116383
116384                 textSection
116385                     .append('pre')
116386                     .attr('class', 'instructions-url')
116387                     .text(_t('settings.custom_data.url.instructions'));
116388
116389                 textSection
116390                     .append('textarea')
116391                     .attr('class', 'field-url')
116392                     .attr('placeholder', _t('settings.custom_data.url.placeholder'))
116393                     .call(utilNoAuto)
116394                     .property('value', _currSettings.url);
116395
116396
116397                 // insert a cancel button
116398                 var buttonSection = modal.select('.modal-section.buttons');
116399
116400                 buttonSection
116401                     .insert('button', '.ok-button')
116402                     .attr('class', 'button cancel-button secondary-action')
116403                     .text(_t('confirm.cancel'));
116404
116405
116406                 buttonSection.select('.cancel-button')
116407                     .on('click.cancel', clickCancel);
116408
116409                 buttonSection.select('.ok-button')
116410                     .attr('disabled', isSaveDisabled)
116411                     .on('click.save', clickSave);
116412
116413
116414                 function isSaveDisabled() {
116415                     return null;
116416                 }
116417
116418
116419                 // restore the original url
116420                 function clickCancel() {
116421                     textSection.select('.field-url').property('value', _origSettings.url);
116422                     corePreferences('settings-custom-data-url', _origSettings.url);
116423                     this.blur();
116424                     modal.close();
116425                 }
116426
116427                 // accept the current url
116428                 function clickSave() {
116429                     _currSettings.url = textSection.select('.field-url').property('value').trim();
116430
116431                     // one or the other but not both
116432                     if (_currSettings.url) { _currSettings.fileList = null; }
116433                     if (_currSettings.fileList) { _currSettings.url = ''; }
116434
116435                     corePreferences('settings-custom-data-url', _currSettings.url);
116436                     this.blur();
116437                     modal.close();
116438                     dispatch$1.call('change', this, _currSettings);
116439                 }
116440             }
116441
116442             return utilRebind(render, dispatch$1, 'on');
116443         }
116444
116445         function uiSectionDataLayers(context) {
116446
116447             var settingsCustomData = uiSettingsCustomData(context)
116448                 .on('change', customChanged);
116449
116450             var layers = context.layers();
116451
116452             var section = uiSection('data-layers', context)
116453                 .title(_t('map_data.data_layers'))
116454                 .disclosureContent(renderDisclosureContent);
116455
116456             function renderDisclosureContent(selection) {
116457                 var container = selection.selectAll('.data-layer-container')
116458                     .data([0]);
116459
116460                 container.enter()
116461                     .append('div')
116462                     .attr('class', 'data-layer-container')
116463                     .merge(container)
116464                     .call(drawOsmItems)
116465                     .call(drawQAItems)
116466                     .call(drawCustomDataItems)
116467                     .call(drawVectorItems)      // Beta - Detroit mapping challenge
116468                     .call(drawPanelItems);
116469             }
116470
116471             function showsLayer(which) {
116472                 var layer = layers.layer(which);
116473                 if (layer) {
116474                     return layer.enabled();
116475                 }
116476                 return false;
116477             }
116478
116479             function setLayer(which, enabled) {
116480                 // Don't allow layer changes while drawing - #6584
116481                 var mode = context.mode();
116482                 if (mode && /^draw/.test(mode.id)) { return; }
116483
116484                 var layer = layers.layer(which);
116485                 if (layer) {
116486                     layer.enabled(enabled);
116487
116488                     if (!enabled && (which === 'osm' || which === 'notes')) {
116489                         context.enter(modeBrowse(context));
116490                     }
116491                 }
116492             }
116493
116494             function toggleLayer(which) {
116495                 setLayer(which, !showsLayer(which));
116496             }
116497
116498             function drawOsmItems(selection) {
116499                 var osmKeys = ['osm', 'notes'];
116500                 var osmLayers = layers.all().filter(function(obj) { return osmKeys.indexOf(obj.id) !== -1; });
116501
116502                 var ul = selection
116503                     .selectAll('.layer-list-osm')
116504                     .data([0]);
116505
116506                 ul = ul.enter()
116507                     .append('ul')
116508                     .attr('class', 'layer-list layer-list-osm')
116509                     .merge(ul);
116510
116511                 var li = ul.selectAll('.list-item')
116512                     .data(osmLayers);
116513
116514                 li.exit()
116515                     .remove();
116516
116517                 var liEnter = li.enter()
116518                     .append('li')
116519                     .attr('class', function(d) { return 'list-item list-item-' + d.id; });
116520
116521                 var labelEnter = liEnter
116522                     .append('label')
116523                     .each(function(d) {
116524                         if (d.id === 'osm') {
116525                             select(this)
116526                                 .call(uiTooltip()
116527                                     .title(_t('map_data.layers.' + d.id + '.tooltip'))
116528                                     .keys([uiCmd('⌥' + _t('area_fill.wireframe.key'))])
116529                                     .placement('bottom')
116530                                 );
116531                         } else {
116532                             select(this)
116533                                 .call(uiTooltip()
116534                                     .title(_t('map_data.layers.' + d.id + '.tooltip'))
116535                                     .placement('bottom')
116536                                 );
116537                         }
116538                     });
116539
116540                 labelEnter
116541                     .append('input')
116542                     .attr('type', 'checkbox')
116543                     .on('change', function(d) { toggleLayer(d.id); });
116544
116545                 labelEnter
116546                     .append('span')
116547                     .text(function(d) { return _t('map_data.layers.' + d.id + '.title'); });
116548
116549
116550                 // Update
116551                 li
116552                     .merge(liEnter)
116553                     .classed('active', function (d) { return d.layer.enabled(); })
116554                     .selectAll('input')
116555                     .property('checked', function (d) { return d.layer.enabled(); });
116556             }
116557
116558             function drawQAItems(selection) {
116559                 var qaKeys = ['keepRight', 'improveOSM', 'osmose'];
116560                 var qaLayers = layers.all().filter(function(obj) { return qaKeys.indexOf(obj.id) !== -1; });
116561
116562                 var ul = selection
116563                     .selectAll('.layer-list-qa')
116564                     .data([0]);
116565
116566                 ul = ul.enter()
116567                     .append('ul')
116568                     .attr('class', 'layer-list layer-list-qa')
116569                     .merge(ul);
116570
116571                 var li = ul.selectAll('.list-item')
116572                     .data(qaLayers);
116573
116574                 li.exit()
116575                     .remove();
116576
116577                 var liEnter = li.enter()
116578                     .append('li')
116579                     .attr('class', function(d) { return 'list-item list-item-' + d.id; });
116580
116581                 var labelEnter = liEnter
116582                     .append('label')
116583                     .each(function(d) {
116584                         select(this)
116585                             .call(uiTooltip()
116586                                 .title(_t('map_data.layers.' + d.id + '.tooltip'))
116587                                 .placement('bottom')
116588                             );
116589                     });
116590
116591                 labelEnter
116592                     .append('input')
116593                     .attr('type', 'checkbox')
116594                     .on('change', function(d) { toggleLayer(d.id); });
116595
116596                 labelEnter
116597                     .append('span')
116598                     .text(function(d) { return _t('map_data.layers.' + d.id + '.title'); });
116599
116600
116601                 // Update
116602                 li
116603                     .merge(liEnter)
116604                     .classed('active', function (d) { return d.layer.enabled(); })
116605                     .selectAll('input')
116606                     .property('checked', function (d) { return d.layer.enabled(); });
116607             }
116608
116609             // Beta feature - sample vector layers to support Detroit Mapping Challenge
116610             // https://github.com/osmus/detroit-mapping-challenge
116611             function drawVectorItems(selection) {
116612                 var dataLayer = layers.layer('data');
116613                 var vtData = [
116614                     {
116615                         name: 'Detroit Neighborhoods/Parks',
116616                         src: 'neighborhoods-parks',
116617                         tooltip: 'Neighborhood boundaries and parks as compiled by City of Detroit in concert with community groups.',
116618                         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'
116619                     }, {
116620                         name: 'Detroit Composite POIs',
116621                         src: 'composite-poi',
116622                         tooltip: 'Fire Inspections, Business Licenses, and other public location data collated from the City of Detroit.',
116623                         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'
116624                     }, {
116625                         name: 'Detroit All-The-Places POIs',
116626                         src: 'alltheplaces-poi',
116627                         tooltip: 'Public domain business location data created by web scrapers.',
116628                         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'
116629                     }
116630                 ];
116631
116632                 // Only show this if the map is around Detroit..
116633                 var detroit = geoExtent([-83.5, 42.1], [-82.8, 42.5]);
116634                 var showVectorItems = (context.map().zoom() > 9 && detroit.contains(context.map().center()));
116635
116636                 var container = selection.selectAll('.vectortile-container')
116637                     .data(showVectorItems ? [0] : []);
116638
116639                 container.exit()
116640                     .remove();
116641
116642                 var containerEnter = container.enter()
116643                     .append('div')
116644                     .attr('class', 'vectortile-container');
116645
116646                 containerEnter
116647                     .append('h4')
116648                     .attr('class', 'vectortile-header')
116649                     .text('Detroit Vector Tiles (Beta)');
116650
116651                 containerEnter
116652                     .append('ul')
116653                     .attr('class', 'layer-list layer-list-vectortile');
116654
116655                 containerEnter
116656                     .append('div')
116657                     .attr('class', 'vectortile-footer')
116658                     .append('a')
116659                     .attr('target', '_blank')
116660                     .attr('tabindex', -1)
116661                     .call(svgIcon('#iD-icon-out-link', 'inline'))
116662                     .attr('href', 'https://github.com/osmus/detroit-mapping-challenge')
116663                     .append('span')
116664                     .text('About these layers');
116665
116666                 container = container
116667                     .merge(containerEnter);
116668
116669
116670                 var ul = container.selectAll('.layer-list-vectortile');
116671
116672                 var li = ul.selectAll('.list-item')
116673                     .data(vtData);
116674
116675                 li.exit()
116676                     .remove();
116677
116678                 var liEnter = li.enter()
116679                     .append('li')
116680                     .attr('class', function(d) { return 'list-item list-item-' + d.src; });
116681
116682                 var labelEnter = liEnter
116683                     .append('label')
116684                     .each(function(d) {
116685                         select(this).call(
116686                             uiTooltip().title(d.tooltip).placement('top')
116687                         );
116688                     });
116689
116690                 labelEnter
116691                     .append('input')
116692                     .attr('type', 'radio')
116693                     .attr('name', 'vectortile')
116694                     .on('change', selectVTLayer);
116695
116696                 labelEnter
116697                     .append('span')
116698                     .text(function(d) { return d.name; });
116699
116700                 // Update
116701                 li
116702                     .merge(liEnter)
116703                     .classed('active', isVTLayerSelected)
116704                     .selectAll('input')
116705                     .property('checked', isVTLayerSelected);
116706
116707
116708                 function isVTLayerSelected(d) {
116709                     return dataLayer && dataLayer.template() === d.template;
116710                 }
116711
116712                 function selectVTLayer(d) {
116713                     corePreferences('settings-custom-data-url', d.template);
116714                     if (dataLayer) {
116715                         dataLayer.template(d.template, d.src);
116716                         dataLayer.enabled(true);
116717                     }
116718                 }
116719             }
116720
116721             function drawCustomDataItems(selection) {
116722                 var dataLayer = layers.layer('data');
116723                 var hasData = dataLayer && dataLayer.hasData();
116724                 var showsData = hasData && dataLayer.enabled();
116725
116726                 var ul = selection
116727                     .selectAll('.layer-list-data')
116728                     .data(dataLayer ? [0] : []);
116729
116730                 // Exit
116731                 ul.exit()
116732                     .remove();
116733
116734                 // Enter
116735                 var ulEnter = ul.enter()
116736                     .append('ul')
116737                     .attr('class', 'layer-list layer-list-data');
116738
116739                 var liEnter = ulEnter
116740                     .append('li')
116741                     .attr('class', 'list-item-data');
116742
116743                 var labelEnter = liEnter
116744                     .append('label')
116745                     .call(uiTooltip()
116746                         .title(_t('map_data.layers.custom.tooltip'))
116747                         .placement('top')
116748                     );
116749
116750                 labelEnter
116751                     .append('input')
116752                     .attr('type', 'checkbox')
116753                     .on('change', function() { toggleLayer('data'); });
116754
116755                 labelEnter
116756                     .append('span')
116757                     .text(_t('map_data.layers.custom.title'));
116758
116759                 liEnter
116760                     .append('button')
116761                     .call(uiTooltip()
116762                         .title(_t('settings.custom_data.tooltip'))
116763                         .placement((_mainLocalizer.textDirection() === 'rtl') ? 'right' : 'left')
116764                     )
116765                     .on('click', editCustom)
116766                     .call(svgIcon('#iD-icon-more'));
116767
116768                 liEnter
116769                     .append('button')
116770                     .call(uiTooltip()
116771                         .title(_t('map_data.layers.custom.zoom'))
116772                         .placement((_mainLocalizer.textDirection() === 'rtl') ? 'right' : 'left')
116773                     )
116774                     .on('click', function() {
116775                         event.preventDefault();
116776                         event.stopPropagation();
116777                         dataLayer.fitZoom();
116778                     })
116779                     .call(svgIcon('#iD-icon-framed-dot'));
116780
116781                 // Update
116782                 ul = ul
116783                     .merge(ulEnter);
116784
116785                 ul.selectAll('.list-item-data')
116786                     .classed('active', showsData)
116787                     .selectAll('label')
116788                     .classed('deemphasize', !hasData)
116789                     .selectAll('input')
116790                     .property('disabled', !hasData)
116791                     .property('checked', showsData);
116792             }
116793
116794             function editCustom() {
116795                 event.preventDefault();
116796                 context.container()
116797                     .call(settingsCustomData);
116798             }
116799
116800             function customChanged(d) {
116801                 var dataLayer = layers.layer('data');
116802
116803                 if (d && d.url) {
116804                     dataLayer.url(d.url);
116805                 } else if (d && d.fileList) {
116806                     dataLayer.fileList(d.fileList);
116807                 }
116808             }
116809
116810
116811             function drawPanelItems(selection) {
116812
116813                 var panelsListEnter = selection.selectAll('.md-extras-list')
116814                     .data([0])
116815                     .enter()
116816                     .append('ul')
116817                     .attr('class', 'layer-list md-extras-list');
116818
116819                 var historyPanelLabelEnter = panelsListEnter
116820                     .append('li')
116821                     .attr('class', 'history-panel-toggle-item')
116822                     .append('label')
116823                     .call(uiTooltip()
116824                         .title(_t('map_data.history_panel.tooltip'))
116825                         .keys([uiCmd('⌘⇧' + _t('info_panels.history.key'))])
116826                         .placement('top')
116827                     );
116828
116829                 historyPanelLabelEnter
116830                     .append('input')
116831                     .attr('type', 'checkbox')
116832                     .on('change', function() {
116833                         event.preventDefault();
116834                         context.ui().info.toggle('history');
116835                     });
116836
116837                 historyPanelLabelEnter
116838                     .append('span')
116839                     .text(_t('map_data.history_panel.title'));
116840
116841                 var measurementPanelLabelEnter = panelsListEnter
116842                     .append('li')
116843                     .attr('class', 'measurement-panel-toggle-item')
116844                     .append('label')
116845                     .call(uiTooltip()
116846                         .title(_t('map_data.measurement_panel.tooltip'))
116847                         .keys([uiCmd('⌘⇧' + _t('info_panels.measurement.key'))])
116848                         .placement('top')
116849                     );
116850
116851                 measurementPanelLabelEnter
116852                     .append('input')
116853                     .attr('type', 'checkbox')
116854                     .on('change', function() {
116855                         event.preventDefault();
116856                         context.ui().info.toggle('measurement');
116857                     });
116858
116859                 measurementPanelLabelEnter
116860                     .append('span')
116861                     .text(_t('map_data.measurement_panel.title'));
116862             }
116863
116864             context.layers().on('change.uiSectionDataLayers', section.reRender);
116865
116866             context.map()
116867                 .on('move.uiSectionDataLayers',
116868                     debounce(function() {
116869                         // Detroit layers may have moved in or out of view
116870                         window.requestIdleCallback(section.reRender);
116871                     }, 1000)
116872                 );
116873
116874             return section;
116875         }
116876
116877         function uiSectionMapFeatures(context) {
116878
116879             var _features = context.features().keys();
116880
116881             var section = uiSection('map-features', context)
116882                 .title(_t('map_data.map_features'))
116883                 .disclosureContent(renderDisclosureContent)
116884                 .expandedByDefault(false);
116885
116886             function renderDisclosureContent(selection) {
116887
116888                 var container = selection.selectAll('.layer-feature-list-container')
116889                     .data([0]);
116890
116891                 var containerEnter = container.enter()
116892                     .append('div')
116893                     .attr('class', 'layer-feature-list-container');
116894
116895                 containerEnter
116896                     .append('ul')
116897                     .attr('class', 'layer-list layer-feature-list');
116898
116899                 var footer = containerEnter
116900                     .append('div')
116901                     .attr('class', 'feature-list-links section-footer');
116902
116903                 footer
116904                     .append('a')
116905                     .attr('class', 'feature-list-link')
116906                     .attr('href', '#')
116907                     .text(_t('issues.enable_all'))
116908                     .on('click', function() {
116909                         context.features().enableAll();
116910                     });
116911
116912                 footer
116913                     .append('a')
116914                     .attr('class', 'feature-list-link')
116915                     .attr('href', '#')
116916                     .text(_t('issues.disable_all'))
116917                     .on('click', function() {
116918                         context.features().disableAll();
116919                     });
116920
116921                 // Update
116922                 container = container
116923                     .merge(containerEnter);
116924
116925                 container.selectAll('.layer-feature-list')
116926                     .call(drawListItems, _features, 'checkbox', 'feature', clickFeature, showsFeature);
116927             }
116928
116929             function drawListItems(selection, data, type, name, change, active) {
116930                 var items = selection.selectAll('li')
116931                     .data(data);
116932
116933                 // Exit
116934                 items.exit()
116935                     .remove();
116936
116937                 // Enter
116938                 var enter = items.enter()
116939                     .append('li')
116940                     .call(uiTooltip()
116941                         .title(function(d) {
116942                             var tip = _t(name + '.' + d + '.tooltip');
116943                             if (autoHiddenFeature(d)) {
116944                                 var msg = showsLayer('osm') ? _t('map_data.autohidden') : _t('map_data.osmhidden');
116945                                 tip += '<div>' + msg + '</div>';
116946                             }
116947                             return tip;
116948                         })
116949                         .placement('top')
116950                     );
116951
116952                 var label = enter
116953                     .append('label');
116954
116955                 label
116956                     .append('input')
116957                     .attr('type', type)
116958                     .attr('name', name)
116959                     .on('change', change);
116960
116961                 label
116962                     .append('span')
116963                     .text(function(d) { return _t(name + '.' + d + '.description'); });
116964
116965                 // Update
116966                 items = items
116967                     .merge(enter);
116968
116969                 items
116970                     .classed('active', active)
116971                     .selectAll('input')
116972                     .property('checked', active)
116973                     .property('indeterminate', autoHiddenFeature);
116974             }
116975
116976             function autoHiddenFeature(d) {
116977                 return context.features().autoHidden(d);
116978             }
116979
116980             function showsFeature(d) {
116981                 return context.features().enabled(d);
116982             }
116983
116984             function clickFeature(d) {
116985                 context.features().toggle(d);
116986             }
116987
116988             function showsLayer(id) {
116989                 var layer = context.layers().layer(id);
116990                 return layer && layer.enabled();
116991             }
116992
116993             // add listeners
116994             context.features()
116995                 .on('change.map_features', section.reRender);
116996
116997             return section;
116998         }
116999
117000         function uiSectionMapStyleOptions(context) {
117001
117002             var section = uiSection('fill-area', context)
117003                 .title(_t('map_data.style_options'))
117004                 .disclosureContent(renderDisclosureContent)
117005                 .expandedByDefault(false);
117006
117007             function renderDisclosureContent(selection) {
117008                 var container = selection.selectAll('.layer-fill-list')
117009                     .data([0]);
117010
117011                 container.enter()
117012                     .append('ul')
117013                     .attr('class', 'layer-list layer-fill-list')
117014                     .merge(container)
117015                     .call(drawListItems, context.map().areaFillOptions, 'radio', 'area_fill', setFill, isActiveFill);
117016
117017                 var container2 = selection.selectAll('.layer-visual-diff-list')
117018                     .data([0]);
117019
117020                 container2.enter()
117021                     .append('ul')
117022                     .attr('class', 'layer-list layer-visual-diff-list')
117023                     .merge(container2)
117024                     .call(drawListItems, ['highlight_edits'], 'checkbox', 'visual_diff', toggleHighlightEdited, function() {
117025                         return context.surface().classed('highlight-edited');
117026                     });
117027             }
117028
117029             function drawListItems(selection, data, type, name, change, active) {
117030                 var items = selection.selectAll('li')
117031                     .data(data);
117032
117033                 // Exit
117034                 items.exit()
117035                     .remove();
117036
117037                 // Enter
117038                 var enter = items.enter()
117039                     .append('li')
117040                     .call(uiTooltip()
117041                         .title(function(d) {
117042                             return _t(name + '.' + d + '.tooltip');
117043                         })
117044                         .keys(function(d) {
117045                             var key = (d === 'wireframe' ? _t('area_fill.wireframe.key') : null);
117046                             if (d === 'highlight_edits') { key = _t('map_data.highlight_edits.key'); }
117047                             return key ? [key] : null;
117048                         })
117049                         .placement('top')
117050                     );
117051
117052                 var label = enter
117053                     .append('label');
117054
117055                 label
117056                     .append('input')
117057                     .attr('type', type)
117058                     .attr('name', name)
117059                     .on('change', change);
117060
117061                 label
117062                     .append('span')
117063                     .text(function(d) { return _t(name + '.' + d + '.description'); });
117064
117065                 // Update
117066                 items = items
117067                     .merge(enter);
117068
117069                 items
117070                     .classed('active', active)
117071                     .selectAll('input')
117072                     .property('checked', active)
117073                     .property('indeterminate', false);
117074             }
117075
117076             function isActiveFill(d) {
117077                 return context.map().activeAreaFill() === d;
117078             }
117079
117080             function toggleHighlightEdited() {
117081                 event.preventDefault();
117082                 context.map().toggleHighlightEdited();
117083             }
117084
117085             function setFill(d) {
117086                 context.map().activeAreaFill(d);
117087             }
117088
117089             context.map()
117090                 .on('changeHighlighting.ui_style, changeAreaFill.ui_style', section.reRender);
117091
117092             return section;
117093         }
117094
117095         function uiSectionPhotoOverlays(context) {
117096
117097             var layers = context.layers();
117098
117099             var section = uiSection('photo-overlays', context)
117100                 .title(_t('photo_overlays.title'))
117101                 .disclosureContent(renderDisclosureContent)
117102                 .expandedByDefault(false);
117103
117104             function renderDisclosureContent(selection) {
117105                 var container = selection.selectAll('.photo-overlay-container')
117106                     .data([0]);
117107
117108                 container.enter()
117109                     .append('div')
117110                     .attr('class', 'photo-overlay-container')
117111                     .merge(container)
117112                     .call(drawPhotoItems)
117113                     .call(drawPhotoTypeItems);
117114             }
117115
117116             function drawPhotoItems(selection) {
117117                 var photoKeys = context.photos().overlayLayerIDs();
117118                 var photoLayers = layers.all().filter(function(obj) { return photoKeys.indexOf(obj.id) !== -1; });
117119                 var data = photoLayers.filter(function(obj) { return obj.layer.supported(); });
117120
117121                 function layerSupported(d) {
117122                     return d.layer && d.layer.supported();
117123                 }
117124                 function layerEnabled(d) {
117125                     return layerSupported(d) && d.layer.enabled();
117126                 }
117127
117128                 var ul = selection
117129                     .selectAll('.layer-list-photos')
117130                     .data([0]);
117131
117132                 ul = ul.enter()
117133                     .append('ul')
117134                     .attr('class', 'layer-list layer-list-photos')
117135                     .merge(ul);
117136
117137                 var li = ul.selectAll('.list-item-photos')
117138                     .data(data);
117139
117140                 li.exit()
117141                     .remove();
117142
117143                 var liEnter = li.enter()
117144                     .append('li')
117145                     .attr('class', function(d) {
117146                         var classes = 'list-item-photos list-item-' + d.id;
117147                         if (d.id === 'mapillary-signs' || d.id === 'mapillary-map-features') {
117148                             classes += ' indented';
117149                         }
117150                         return classes;
117151                     });
117152
117153                 var labelEnter = liEnter
117154                     .append('label')
117155                     .each(function(d) {
117156                         var titleID;
117157                         if (d.id === 'mapillary-signs') { titleID = 'mapillary.signs.tooltip'; }
117158                         else if (d.id === 'mapillary') { titleID = 'mapillary_images.tooltip'; }
117159                         else if (d.id === 'openstreetcam') { titleID = 'openstreetcam_images.tooltip'; }
117160                         else { titleID = d.id.replace(/-/g, '_') + '.tooltip'; }
117161                         select(this)
117162                             .call(uiTooltip()
117163                                 .title(_t(titleID))
117164                                 .placement('top')
117165                             );
117166                     });
117167
117168                 labelEnter
117169                     .append('input')
117170                     .attr('type', 'checkbox')
117171                     .on('change', function(d) { toggleLayer(d.id); });
117172
117173                 labelEnter
117174                     .append('span')
117175                     .text(function(d) {
117176                         var id = d.id;
117177                         if (id === 'mapillary-signs') { id = 'photo_overlays.traffic_signs'; }
117178                         return _t(id.replace(/-/g, '_') + '.title');
117179                     });
117180
117181
117182                 // Update
117183                 li
117184                     .merge(liEnter)
117185                     .classed('active', layerEnabled)
117186                     .selectAll('input')
117187                     .property('checked', layerEnabled);
117188             }
117189
117190             function drawPhotoTypeItems(selection) {
117191                 var data = context.photos().allPhotoTypes();
117192
117193                 function typeEnabled(d) {
117194                     return context.photos().showsPhotoType(d);
117195                 }
117196
117197                 var ul = selection
117198                     .selectAll('.layer-list-photo-types')
117199                     .data(context.photos().shouldFilterByPhotoType() ? [0] : []);
117200
117201                 ul.exit()
117202                     .remove();
117203
117204                 ul = ul.enter()
117205                     .append('ul')
117206                     .attr('class', 'layer-list layer-list-photo-types')
117207                     .merge(ul);
117208
117209                 var li = ul.selectAll('.list-item-photo-types')
117210                     .data(data);
117211
117212                 li.exit()
117213                     .remove();
117214
117215                 var liEnter = li.enter()
117216                     .append('li')
117217                     .attr('class', function(d) {
117218                         return 'list-item-photo-types list-item-' + d;
117219                     });
117220
117221                 var labelEnter = liEnter
117222                     .append('label')
117223                     .each(function(d) {
117224                         select(this)
117225                             .call(uiTooltip()
117226                                 .title(_t('photo_overlays.photo_type.' + d + '.tooltip'))
117227                                 .placement('top')
117228                             );
117229                     });
117230
117231                 labelEnter
117232                     .append('input')
117233                     .attr('type', 'checkbox')
117234                     .on('change', function(d) {
117235                         context.photos().togglePhotoType(d);
117236                     });
117237
117238                 labelEnter
117239                     .append('span')
117240                     .text(function(d) {
117241                         return _t('photo_overlays.photo_type.' + d + '.title');
117242                     });
117243
117244
117245                 // Update
117246                 li
117247                     .merge(liEnter)
117248                     .classed('active', typeEnabled)
117249                     .selectAll('input')
117250                     .property('checked', typeEnabled);
117251             }
117252
117253             function toggleLayer(which) {
117254                 setLayer(which, !showsLayer(which));
117255             }
117256
117257             function showsLayer(which) {
117258                 var layer = layers.layer(which);
117259                 if (layer) {
117260                     return layer.enabled();
117261                 }
117262                 return false;
117263             }
117264
117265             function setLayer(which, enabled) {
117266                 var layer = layers.layer(which);
117267                 if (layer) {
117268                     layer.enabled(enabled);
117269                 }
117270             }
117271
117272             context.layers().on('change.uiSectionPhotoOverlays', section.reRender);
117273             context.photos().on('change.uiSectionPhotoOverlays', section.reRender);
117274
117275             return section;
117276         }
117277
117278         function uiPaneMapData(context) {
117279
117280             var mapDataPane = uiPane('map-data', context)
117281                 .key(_t('map_data.key'))
117282                 .title(_t('map_data.title'))
117283                 .description(_t('map_data.description'))
117284                 .iconName('iD-icon-data')
117285                 .sections([
117286                     uiSectionDataLayers(context),
117287                     uiSectionPhotoOverlays(context),
117288                     uiSectionMapStyleOptions(context),
117289                     uiSectionMapFeatures(context)
117290                 ]);
117291
117292             return mapDataPane;
117293         }
117294
117295         function uiSectionPrivacy(context) {
117296
117297             var section = uiSection('preferences-third-party', context)
117298               .title(_t('preferences.privacy.title'))
117299               .disclosureContent(renderDisclosureContent);
117300
117301             var _showThirdPartyIcons = corePreferences('preferences.privacy.thirdpartyicons') || 'true';
117302
117303             function renderDisclosureContent(selection) {
117304               // enter
117305               var privacyOptionsListEnter = selection.selectAll('.privacy-options-list')
117306                 .data([0])
117307                 .enter()
117308                 .append('ul')
117309                 .attr('class', 'layer-list privacy-options-list');
117310
117311               var thirdPartyIconsEnter = privacyOptionsListEnter
117312                 .append('li')
117313                 .attr('class', 'privacy-third-party-icons-item')
117314                 .append('label')
117315                 .call(uiTooltip()
117316                   .title(_t('preferences.privacy.third_party_icons.tooltip'))
117317                   .placement('bottom')
117318                 );
117319
117320               thirdPartyIconsEnter
117321                 .append('input')
117322                 .attr('type', 'checkbox')
117323                 .on('change', function () {
117324                   event.preventDefault();
117325                   _showThirdPartyIcons = (_showThirdPartyIcons === 'true') ? 'false' : 'true';
117326                   corePreferences('preferences.privacy.thirdpartyicons', _showThirdPartyIcons);
117327                   update();
117328                 });
117329
117330               thirdPartyIconsEnter
117331                 .append('span')
117332                 .text(_t('preferences.privacy.third_party_icons.description'));
117333
117334
117335               // Privacy Policy link
117336               selection.selectAll('.privacy-link')
117337                 .data([0])
117338                 .enter()
117339                 .append('div')
117340                 .attr('class', 'privacy-link')
117341                 .append('a')
117342                 .attr('target', '_blank')
117343                 .call(svgIcon('#iD-icon-out-link', 'inline'))
117344                 .attr('href', 'https://github.com/openstreetmap/iD/blob/release/PRIVACY.md')
117345                 .append('span')
117346                 .text(_t('preferences.privacy.privacy_link'));
117347
117348               update();
117349
117350
117351               function update() {
117352                 selection.selectAll('.privacy-third-party-icons-item')
117353                   .classed('active', (_showThirdPartyIcons === 'true'))
117354                   .select('input')
117355                   .property('checked', (_showThirdPartyIcons === 'true'));
117356               }
117357             }
117358
117359             return section;
117360         }
117361
117362         function uiPanePreferences(context) {
117363
117364           var preferencesPane = uiPane('preferences', context)
117365             .key(_t('preferences.key'))
117366             .title(_t('preferences.title'))
117367             .description(_t('preferences.description'))
117368             .iconName('fas-user-cog')
117369             .sections([
117370                 uiSectionPrivacy(context)
117371             ]);
117372
117373           return preferencesPane;
117374         }
117375
117376         function uiInit(context) {
117377             var _initCounter = 0;
117378             var _needWidth = {};
117379
117380             var _lastPointerType;
117381
117382
117383             function render(container) {
117384
117385                 container
117386                     .on('click.ui', function() {
117387                         // we're only concerned with the primary mouse button
117388                         if (event.button !== 0) { return; }
117389
117390                         if (!event.composedPath) { return; }
117391
117392                         // some targets have default click events we don't want to override
117393                         var isOkayTarget = event.composedPath().some(function(node) {
117394                             // we only care about element nodes
117395                             return node.nodeType === 1 &&
117396                                 // clicking <input> focuses it and/or changes a value
117397                                 (node.nodeName === 'INPUT' ||
117398                                 // clicking <label> affects its <input> by default
117399                                 node.nodeName === 'LABEL' ||
117400                                 // clicking <a> opens a hyperlink by default
117401                                 node.nodeName === 'A');
117402                         });
117403                         if (isOkayTarget) { return; }
117404
117405                         // disable double-tap-to-zoom on touchscreens
117406                         event.preventDefault();
117407                     });
117408
117409                 var detected = utilDetect();
117410
117411                 // only WebKit supports gesture events
117412                 if ('GestureEvent' in window &&
117413                     // Listening for gesture events on iOS 13.4+ breaks double-tapping,
117414                     // but we only need to do this on desktop Safari anyway. – #7694
117415                     !detected.isMobileWebKit) {
117416
117417                     // On iOS we disable pinch-to-zoom of the UI via the `touch-action`
117418                     // CSS property, but on desktop Safari we need to manually cancel the
117419                     // default gesture events.
117420                     container.on('gesturestart.ui gesturechange.ui gestureend.ui', function() {
117421                         // disable pinch-to-zoom of the UI via multitouch trackpads on macOS Safari
117422                         event.preventDefault();
117423                     });
117424                 }
117425
117426                 if ('PointerEvent' in window) {
117427                     select(window)
117428                         .on('pointerdown.ui pointerup.ui', function() {
117429                             var pointerType = event.pointerType || 'mouse';
117430                             if (_lastPointerType !== pointerType) {
117431                                 _lastPointerType = pointerType;
117432                                 container
117433                                     .attr('pointer', pointerType);
117434                             }
117435                         }, true);
117436                 } else {
117437                     _lastPointerType = 'mouse';
117438                     container
117439                         .attr('pointer', 'mouse');
117440                 }
117441
117442                 container
117443                     .attr('dir', _mainLocalizer.textDirection());
117444
117445                 // setup fullscreen keybindings (no button shown at this time)
117446                 container
117447                     .call(uiFullScreen(context));
117448
117449                 var map = context.map();
117450                 map.redrawEnable(false);  // don't draw until we've set zoom/lat/long
117451
117452                 map
117453                     .on('hitMinZoom.ui', function() {
117454                         ui.flash.text(_t('cannot_zoom'))();
117455                     });
117456
117457                 container
117458                     .append('svg')
117459                     .attr('id', 'ideditor-defs')
117460                     .call(svgDefs(context));
117461
117462                 container
117463                     .append('div')
117464                     .attr('class', 'sidebar')
117465                     .call(ui.sidebar);
117466
117467                 var content = container
117468                     .append('div')
117469                     .attr('class', 'main-content active');
117470
117471                 // Top toolbar
117472                 content
117473                     .append('div')
117474                     .attr('class', 'top-toolbar-wrap')
117475                     .append('div')
117476                     .attr('class', 'top-toolbar fillD')
117477                     .call(uiTopToolbar(context));
117478
117479                 content
117480                     .append('div')
117481                     .attr('class', 'main-map')
117482                     .attr('dir', 'ltr')
117483                     .call(map);
117484
117485                 content
117486                     .append('div')
117487                     .attr('class', 'spinner')
117488                     .call(uiSpinner(context));
117489
117490                 // Add attribution and footer
117491                 var about = content
117492                     .append('div')
117493                     .attr('class', 'map-footer');
117494
117495                 about
117496                     .append('div')
117497                     .attr('class', 'attribution-wrap')
117498                     .attr('dir', 'ltr')
117499                     .call(uiAttribution(context));
117500
117501                 about
117502                     .append('div')
117503                     .attr('class', 'api-status')
117504                     .call(uiStatus(context));
117505
117506
117507                 var footer = about
117508                     .append('div')
117509                     .attr('class', 'map-footer-bar fillD');
117510
117511                 footer
117512                     .append('div')
117513                     .attr('class', 'flash-wrap footer-hide');
117514
117515                 var footerWrap = footer
117516                     .append('div')
117517                     .attr('class', 'main-footer-wrap footer-show');
117518
117519                 footerWrap
117520                     .append('div')
117521                     .attr('class', 'scale-block')
117522                     .call(uiScale(context));
117523
117524                 var aboutList = footerWrap
117525                     .append('div')
117526                     .attr('class', 'info-block')
117527                     .append('ul')
117528                     .attr('class', 'map-footer-list');
117529
117530                 if (!context.embed()) {
117531                     aboutList
117532                         .call(uiAccount(context));
117533                 }
117534
117535                 aboutList
117536                     .append('li')
117537                     .attr('class', 'version')
117538                     .call(uiVersion(context));
117539
117540                 var issueLinks = aboutList
117541                     .append('li');
117542
117543                 issueLinks
117544                     .append('a')
117545                     .attr('target', '_blank')
117546                     .attr('href', 'https://github.com/openstreetmap/iD/issues')
117547                     .call(svgIcon('#iD-icon-bug', 'light'))
117548                     .call(uiTooltip().title(_t('report_a_bug')).placement('top'));
117549
117550                 issueLinks
117551                     .append('a')
117552                     .attr('target', '_blank')
117553                     .attr('href', 'https://github.com/openstreetmap/iD/blob/develop/CONTRIBUTING.md#translating')
117554                     .call(svgIcon('#iD-icon-translate', 'light'))
117555                     .call(uiTooltip().title(_t('help_translate')).placement('top'));
117556
117557                 aboutList
117558                     .append('li')
117559                     .attr('class', 'feature-warning')
117560                     .attr('tabindex', -1)
117561                     .call(uiFeatureInfo(context));
117562
117563                 aboutList
117564                     .append('li')
117565                     .attr('class', 'issues-info')
117566                     .attr('tabindex', -1)
117567                     .call(uiIssuesInfo(context));
117568
117569                 var apiConnections = context.apiConnections();
117570                 if (apiConnections && apiConnections.length > 1) {
117571                     aboutList
117572                         .append('li')
117573                         .attr('class', 'source-switch')
117574                         .attr('tabindex', -1)
117575                         .call(uiSourceSwitch(context)
117576                             .keys(apiConnections)
117577                         );
117578                 }
117579
117580                 aboutList
117581                     .append('li')
117582                     .attr('class', 'user-list')
117583                     .attr('tabindex', -1)
117584                     .call(uiContributors(context));
117585
117586
117587                 // Setup map dimensions and move map to initial center/zoom.
117588                 // This should happen after .main-content and toolbars exist.
117589                 ui.onResize();
117590                 map.redrawEnable(true);
117591
117592                 ui.hash = behaviorHash(context);
117593                 ui.hash();
117594                 if (!ui.hash.hadHash) {
117595                     map.centerZoom([0, 0], 2);
117596                 }
117597
117598
117599                 var overMap = content
117600                     .append('div')
117601                     .attr('class', 'over-map');
117602
117603                 // Map controls
117604                 var controls = overMap
117605                     .append('div')
117606                     .attr('class', 'map-controls');
117607
117608                 controls
117609                     .append('div')
117610                     .attr('class', 'map-control zoombuttons')
117611                     .call(uiZoom(context));
117612
117613                 controls
117614                     .append('div')
117615                     .attr('class', 'map-control zoom-to-selection-control')
117616                     .call(uiZoomToSelection(context));
117617
117618                 controls
117619                     .append('div')
117620                     .attr('class', 'map-control geolocate-control')
117621                     .call(uiGeolocate(context));
117622
117623                 // Add panes
117624                 // This should happen after map is initialized, as some require surface()
117625                 var panes = overMap
117626                     .append('div')
117627                     .attr('class', 'map-panes');
117628
117629                 var uiPanes = [
117630                     uiPaneBackground(context),
117631                     uiPaneMapData(context),
117632                     uiPaneIssues(context),
117633                     uiPanePreferences(context),
117634                     uiPaneHelp(context)
117635                 ];
117636
117637                 uiPanes.forEach(function(pane) {
117638                     controls
117639                         .append('div')
117640                         .attr('class', 'map-control map-pane-control ' + pane.id + '-control')
117641                         .call(pane.renderToggleButton);
117642
117643                     panes
117644                         .call(pane.renderPane);
117645                 });
117646
117647                 ui.info = uiInfo(context);
117648
117649                 // Add absolutely-positioned elements that sit on top of the map
117650                 // This should happen after the map is ready (center/zoom)
117651                 overMap
117652                     .call(uiMapInMap(context))
117653                     .call(ui.info)
117654                     .call(uiNotice(context));
117655
117656
117657                 overMap
117658                     .append('div')
117659                     .attr('class', 'photoviewer')
117660                     .classed('al', true)       // 'al'=left,  'ar'=right
117661                     .classed('hide', true)
117662                     .call(ui.photoviewer);
117663
117664
117665                 // Bind events
117666                 window.onbeforeunload = function() {
117667                     return context.save();
117668                 };
117669                 window.onunload = function() {
117670                     context.history().unlock();
117671                 };
117672
117673                 select(window)
117674                     .on('resize.editor', ui.onResize);
117675
117676
117677                 var panPixels = 80;
117678                 context.keybinding()
117679                     .on('⌫', function() { event.preventDefault(); })
117680                     .on([_t('sidebar.key'), '`', '²', '@'], ui.sidebar.toggle)   // #5663, #6864 - common QWERTY, AZERTY
117681                     .on('←', pan([panPixels, 0]))
117682                     .on('↑', pan([0, panPixels]))
117683                     .on('→', pan([-panPixels, 0]))
117684                     .on('↓', pan([0, -panPixels]))
117685                     .on(uiCmd('⌘←'), pan([map.dimensions()[0], 0]))
117686                     .on(uiCmd('⌘↑'), pan([0, map.dimensions()[1]]))
117687                     .on(uiCmd('⌘→'), pan([-map.dimensions()[0], 0]))
117688                     .on(uiCmd('⌘↓'), pan([0, -map.dimensions()[1]]))
117689                     .on(uiCmd('⌘' + _t('background.key')), function quickSwitch() {
117690                         if (event) {
117691                             event.stopImmediatePropagation();
117692                             event.preventDefault();
117693                         }
117694                         var previousBackground = context.background().findSource(corePreferences('background-last-used-toggle'));
117695                         if (previousBackground) {
117696                             var currentBackground = context.background().baseLayerSource();
117697                             corePreferences('background-last-used-toggle', currentBackground.id);
117698                             corePreferences('background-last-used', previousBackground.id);
117699                             context.background().baseLayerSource(previousBackground);
117700                         }
117701                     })
117702                     .on(_t('area_fill.wireframe.key'), function toggleWireframe() {
117703                         event.preventDefault();
117704                         event.stopPropagation();
117705                         context.map().toggleWireframe();
117706                     })
117707                     .on(uiCmd('⌥' + _t('area_fill.wireframe.key')), function toggleOsmData() {
117708                         event.preventDefault();
117709                         event.stopPropagation();
117710
117711                         // Don't allow layer changes while drawing - #6584
117712                         var mode = context.mode();
117713                         if (mode && /^draw/.test(mode.id)) { return; }
117714
117715                         var layer = context.layers().layer('osm');
117716                         if (layer) {
117717                             layer.enabled(!layer.enabled());
117718                             if (!layer.enabled()) {
117719                                 context.enter(modeBrowse(context));
117720                             }
117721                         }
117722                     })
117723                     .on(_t('map_data.highlight_edits.key'), function toggleHighlightEdited() {
117724                         event.preventDefault();
117725                         context.map().toggleHighlightEdited();
117726                     });
117727
117728                 context
117729                     .on('enter.editor', function(entered) {
117730                         container
117731                             .classed('mode-' + entered.id, true);
117732                     })
117733                     .on('exit.editor', function(exited) {
117734                         container
117735                             .classed('mode-' + exited.id, false);
117736                     });
117737
117738                 context.enter(modeBrowse(context));
117739
117740                 if (!_initCounter++) {
117741                     if (!ui.hash.startWalkthrough) {
117742                         context.container()
117743                             .call(uiSplash(context))
117744                             .call(uiRestore(context));
117745                     }
117746
117747                     context.container()
117748                         .call(uiShortcuts(context));
117749                 }
117750
117751                 var osm = context.connection();
117752                 var auth = uiLoading(context).message(_t('loading_auth')).blocking(true);
117753
117754                 if (osm && auth) {
117755                     osm
117756                         .on('authLoading.ui', function() {
117757                             context.container()
117758                                 .call(auth);
117759                         })
117760                         .on('authDone.ui', function() {
117761                             auth.close();
117762                         });
117763                 }
117764
117765                 _initCounter++;
117766
117767                 if (ui.hash.startWalkthrough) {
117768                     ui.hash.startWalkthrough = false;
117769                     context.container().call(uiIntro(context));
117770                 }
117771
117772
117773                 function pan(d) {
117774                     return function() {
117775                         if (event.shiftKey) { return; }
117776                         if (context.container().select('.combobox').size()) { return; }
117777                         event.preventDefault();
117778                         context.map().pan(d, 100);
117779                     };
117780                 }
117781
117782             }
117783
117784
117785             var ui = {};
117786
117787             var _loadPromise;
117788             // renders the iD interface into the container node
117789             ui.ensureLoaded = function () {
117790
117791                 if (_loadPromise) { return _loadPromise; }
117792
117793                 return _loadPromise = Promise.all([
117794                         // must have strings and presets before loading the UI
117795                         _mainLocalizer.ensureLoaded(),
117796                         _mainPresetIndex.ensureLoaded()
117797                     ])
117798                     .then(function () {
117799                         if (!context.container().empty()) { render(context.container()); }
117800                     })
117801                     .catch(function (err) { return console.error(err); });  // eslint-disable-line
117802             };
117803
117804
117805             // `ui.restart()` will destroy and rebuild the entire iD interface,
117806             // for example to switch the locale while iD is running.
117807             ui.restart = function() {
117808                 context.keybinding().clear();
117809
117810                 _loadPromise = null;
117811
117812                 context.container().selectAll('*').remove();
117813
117814                 ui.ensureLoaded();
117815             };
117816
117817             ui.lastPointerType = function() {
117818                 return _lastPointerType;
117819             };
117820
117821             ui.flash = uiFlash(context);
117822
117823             ui.sidebar = uiSidebar(context);
117824
117825             ui.photoviewer = uiPhotoviewer(context);
117826
117827             ui.onResize = function(withPan) {
117828                 var map = context.map();
117829
117830                 // Recalc dimensions of map and sidebar.. (`true` = force recalc)
117831                 // This will call `getBoundingClientRect` and trigger reflow,
117832                 //  but the values will be cached for later use.
117833                 var mapDimensions = utilGetDimensions(context.container().select('.main-content'), true);
117834                 utilGetDimensions(context.container().select('.sidebar'), true);
117835
117836                 if (withPan !== undefined) {
117837                     map.redrawEnable(false);
117838                     map.pan(withPan);
117839                     map.redrawEnable(true);
117840                 }
117841                 map.dimensions(mapDimensions);
117842
117843                 ui.photoviewer.onMapResize();
117844
117845                 // check if header or footer have overflowed
117846                 ui.checkOverflow('.top-toolbar');
117847                 ui.checkOverflow('.map-footer-bar');
117848
117849                 // Use outdated code so it works on Explorer
117850                 var resizeWindowEvent = document.createEvent('Event');
117851
117852                 resizeWindowEvent.initEvent('resizeWindow', true, true);
117853
117854                 document.dispatchEvent(resizeWindowEvent);
117855             };
117856
117857
117858             // Call checkOverflow when resizing or whenever the contents change.
117859             ui.checkOverflow = function(selector, reset) {
117860                 if (reset) {
117861                     delete _needWidth[selector];
117862                 }
117863
117864                 var element = select(selector);
117865                 var scrollWidth = element.property('scrollWidth');
117866                 var clientWidth = element.property('clientWidth');
117867                 var needed = _needWidth[selector] || scrollWidth;
117868
117869                 if (scrollWidth > clientWidth) {    // overflow happening
117870                     element.classed('narrow', true);
117871                     if (!_needWidth[selector]) {
117872                         _needWidth[selector] = scrollWidth;
117873                     }
117874
117875                 } else if (scrollWidth >= needed) {
117876                     element.classed('narrow', false);
117877                 }
117878             };
117879
117880             ui.togglePanes = function(showPane) {
117881                 var shownPanes = context.container().selectAll('.map-pane.shown');
117882
117883                 var side = _mainLocalizer.textDirection() === 'ltr' ? 'right' : 'left';
117884
117885                 shownPanes
117886                     .classed('shown', false);
117887
117888                 context.container().selectAll('.map-pane-control button')
117889                     .classed('active', false);
117890
117891                 if (showPane) {
117892                     shownPanes
117893                         .style('display', 'none')
117894                         .style(side, '-500px');
117895
117896                     context.container().selectAll('.' + showPane.attr('pane') + '-control button')
117897                         .classed('active', true);
117898
117899                     showPane
117900                         .classed('shown', true)
117901                         .style('display', 'block');
117902                     if (shownPanes.empty()) {
117903                         showPane
117904                             .style('display', 'block')
117905                             .style(side, '-500px')
117906                             .transition()
117907                             .duration(200)
117908                             .style(side, '0px');
117909                     } else {
117910                         showPane
117911                             .style(side, '0px');
117912                     }
117913                 } else {
117914                     shownPanes
117915                         .style('display', 'block')
117916                         .style(side, '0px')
117917                         .transition()
117918                         .duration(200)
117919                         .style(side, '-500px')
117920                         .on('end', function() {
117921                             select(this).style('display', 'none');
117922                         });
117923                 }
117924             };
117925
117926
117927             var _editMenu = uiEditMenu(context);
117928
117929             ui.editMenu = function() {
117930                 return _editMenu;
117931             };
117932
117933             ui.showEditMenu = function(anchorPoint, triggerType, operations) {
117934
117935                 // remove any displayed menu
117936                 ui.closeEditMenu();
117937
117938                 if (!operations && context.mode().operations) { operations = context.mode().operations(); }
117939                 if (!operations || !operations.length) { return; }
117940
117941                 // disable menu if in wide selection, for example
117942                 if (!context.map().editableDataEnabled()) { return; }
117943
117944                 var surfaceNode = context.surface().node();
117945                 if (surfaceNode.focus) {   // FF doesn't support it
117946                     // focus the surface or else clicking off the menu may not trigger modeBrowse
117947                     surfaceNode.focus();
117948                 }
117949
117950                 operations.forEach(function(operation) {
117951                     if (operation.point) { operation.point(anchorPoint); }
117952                 });
117953
117954                 _editMenu
117955                     .anchorLoc(anchorPoint)
117956                     .triggerType(triggerType)
117957                     .operations(operations);
117958
117959                 // render the menu
117960                 context.map().supersurface.call(_editMenu);
117961             };
117962
117963             ui.closeEditMenu = function() {
117964                 // remove any existing menu no matter how it was added
117965                 context.map().supersurface
117966                     .select('.edit-menu').remove();
117967             };
117968
117969
117970             var _saveLoading = select(null);
117971
117972             context.uploader()
117973                 .on('saveStarted.ui', function() {
117974                     _saveLoading = uiLoading(context)
117975                         .message(_t('save.uploading'))
117976                         .blocking(true);
117977                     context.container().call(_saveLoading);  // block input during upload
117978                 })
117979                 .on('saveEnded.ui', function() {
117980                     _saveLoading.close();
117981                     _saveLoading = select(null);
117982                 });
117983
117984             return ui;
117985         }
117986
117987         function coreContext() {
117988           var this$1 = this;
117989
117990           var dispatch$1 = dispatch('enter', 'exit', 'change');
117991           var context = utilRebind({}, dispatch$1, 'on');
117992           var _deferred = new Set();
117993
117994           context.version = '2.18.3';
117995           context.privacyVersion = '20200407';
117996
117997           // iD will alter the hash so cache the parameters intended to setup the session
117998           context.initialHashParams = window.location.hash ? utilStringQs(window.location.hash) : {};
117999
118000           context.isFirstSession = !corePreferences('sawSplash') && !corePreferences('sawPrivacyVersion');
118001
118002
118003           /* Changeset */
118004           // An osmChangeset object. Not loaded until needed.
118005           context.changeset = null;
118006
118007           var _defaultChangesetComment = context.initialHashParams.comment;
118008           var _defaultChangesetSource = context.initialHashParams.source;
118009           var _defaultChangesetHashtags = context.initialHashParams.hashtags;
118010           context.defaultChangesetComment = function(val) {
118011             if (!arguments.length) { return _defaultChangesetComment; }
118012             _defaultChangesetComment = val;
118013             return context;
118014           };
118015           context.defaultChangesetSource = function(val) {
118016             if (!arguments.length) { return _defaultChangesetSource; }
118017             _defaultChangesetSource = val;
118018             return context;
118019           };
118020           context.defaultChangesetHashtags = function(val) {
118021             if (!arguments.length) { return _defaultChangesetHashtags; }
118022             _defaultChangesetHashtags = val;
118023             return context;
118024           };
118025
118026           /* Document title */
118027           /* (typically shown as the label for the browser window/tab) */
118028
118029           // If true, iD will update the title based on what the user is doing
118030           var _setsDocumentTitle = true;
118031           context.setsDocumentTitle = function(val) {
118032             if (!arguments.length) { return _setsDocumentTitle; }
118033             _setsDocumentTitle = val;
118034             return context;
118035           };
118036           // The part of the title that is always the same
118037           var _documentTitleBase = document.title;
118038           context.documentTitleBase = function(val) {
118039             if (!arguments.length) { return _documentTitleBase; }
118040             _documentTitleBase = val;
118041             return context;
118042           };
118043
118044
118045           /* User interface and keybinding */
118046           var _ui;
118047           context.ui = function () { return _ui; };
118048           context.lastPointerType = function () { return _ui.lastPointerType(); };
118049
118050           var _keybinding = utilKeybinding('context');
118051           context.keybinding = function () { return _keybinding; };
118052           select(document).call(_keybinding);
118053
118054
118055           /* Straight accessors. Avoid using these if you can. */
118056           // Instantiate the connection here because it doesn't require passing in
118057           // `context` and it's needed for pre-init calls like `preauth`
118058           var _connection = services.osm;
118059           var _history;
118060           var _validator;
118061           var _uploader;
118062           context.connection = function () { return _connection; };
118063           context.history = function () { return _history; };
118064           context.validator = function () { return _validator; };
118065           context.uploader = function () { return _uploader; };
118066
118067           /* Connection */
118068           context.preauth = function (options) {
118069             if (_connection) {
118070               _connection.switch(options);
118071             }
118072             return context;
118073           };
118074
118075           /* connection options for source switcher (optional) */
118076           var _apiConnections;
118077           context.apiConnections = function(val) {
118078             if (!arguments.length) { return _apiConnections; }
118079             _apiConnections = val;
118080             return context;
118081           };
118082
118083
118084           // A string or array or locale codes to prefer over the browser's settings
118085           context.locale = function(locale) {
118086             if (!arguments.length) { return _mainLocalizer.localeCode(); }
118087             _mainLocalizer.preferredLocaleCodes(locale);
118088             return context;
118089           };
118090
118091
118092           function afterLoad(cid, callback) {
118093             return function (err, result) {
118094               if (err) {
118095                 // 400 Bad Request, 401 Unauthorized, 403 Forbidden..
118096                 if (err.status === 400 || err.status === 401 || err.status === 403) {
118097                   if (_connection) {
118098                     _connection.logout();
118099                   }
118100                 }
118101                 if (typeof callback === 'function') {
118102                   callback(err);
118103                 }
118104                 return;
118105
118106               } else if (_connection && _connection.getConnectionId() !== cid) {
118107                 if (typeof callback === 'function') {
118108                   callback({ message: 'Connection Switched', status: -1 });
118109                 }
118110                 return;
118111
118112               } else {
118113                 _history.merge(result.data, result.extent);
118114                 if (typeof callback === 'function') {
118115                   callback(err, result);
118116                 }
118117                 return;
118118               }
118119             };
118120           }
118121
118122
118123           context.loadTiles = function (projection, callback) {
118124             var handle = window.requestIdleCallback(function () {
118125               _deferred.delete(handle);
118126               if (_connection && context.editableDataEnabled()) {
118127                 var cid = _connection.getConnectionId();
118128                 _connection.loadTiles(projection, afterLoad(cid, callback));
118129               }
118130             });
118131             _deferred.add(handle);
118132           };
118133
118134           context.loadTileAtLoc = function (loc, callback) {
118135             var handle = window.requestIdleCallback(function () {
118136               _deferred.delete(handle);
118137               if (_connection && context.editableDataEnabled()) {
118138                 var cid = _connection.getConnectionId();
118139                 _connection.loadTileAtLoc(loc, afterLoad(cid, callback));
118140               }
118141             });
118142             _deferred.add(handle);
118143           };
118144
118145           context.loadEntity = function (entityID, callback) {
118146             if (_connection) {
118147               var cid = _connection.getConnectionId();
118148               _connection.loadEntity(entityID, afterLoad(cid, callback));
118149             }
118150           };
118151
118152           context.zoomToEntity = function (entityID, zoomTo) {
118153             if (zoomTo !== false) {
118154               context.loadEntity(entityID, function (err, result) {
118155                 if (err) { return; }
118156                 var entity = result.data.find(function (e) { return e.id === entityID; });
118157                 if (entity) {
118158                   _map.zoomTo(entity);
118159                 }
118160               });
118161             }
118162
118163             _map.on('drawn.zoomToEntity', function () {
118164               if (!context.hasEntity(entityID)) { return; }
118165               _map.on('drawn.zoomToEntity', null);
118166               context.on('enter.zoomToEntity', null);
118167               context.enter(modeSelect(context, [entityID]));
118168             });
118169
118170             context.on('enter.zoomToEntity', function () {
118171               if (_mode.id !== 'browse') {
118172                 _map.on('drawn.zoomToEntity', null);
118173                 context.on('enter.zoomToEntity', null);
118174               }
118175             });
118176           };
118177
118178           var _minEditableZoom = 16;
118179           context.minEditableZoom = function(val) {
118180             if (!arguments.length) { return _minEditableZoom; }
118181             _minEditableZoom = val;
118182             if (_connection) {
118183               _connection.tileZoom(val);
118184             }
118185             return context;
118186           };
118187
118188           // String length limits in Unicode characters, not JavaScript UTF-16 code units
118189           context.maxCharsForTagKey = function () { return 255; };
118190           context.maxCharsForTagValue = function () { return 255; };
118191           context.maxCharsForRelationRole = function () { return 255; };
118192
118193           function cleanOsmString(val, maxChars) {
118194             // be lenient with input
118195             if (val === undefined || val === null) {
118196               val = '';
118197             } else {
118198               val = val.toString();
118199             }
118200
118201             // remove whitespace
118202             val = val.trim();
118203
118204             // use the canonical form of the string
118205             if (val.normalize) { val = val.normalize('NFC'); }
118206
118207             // trim to the number of allowed characters
118208             return utilUnicodeCharsTruncated(val, maxChars);
118209           }
118210           context.cleanTagKey = function (val) { return cleanOsmString(val, context.maxCharsForTagKey()); };
118211           context.cleanTagValue = function (val) { return cleanOsmString(val, context.maxCharsForTagValue()); };
118212           context.cleanRelationRole = function (val) { return cleanOsmString(val, context.maxCharsForRelationRole()); };
118213
118214
118215           /* History */
118216           var _inIntro = false;
118217           context.inIntro = function(val) {
118218             if (!arguments.length) { return _inIntro; }
118219             _inIntro = val;
118220             return context;
118221           };
118222
118223           // Immediately save the user's history to localstorage, if possible
118224           // This is called someteimes, but also on the `window.onbeforeunload` handler
118225           context.save = function () {
118226             // no history save, no message onbeforeunload
118227             if (_inIntro || context.container().select('.modal').size()) { return; }
118228
118229             var canSave;
118230             if (_mode && _mode.id === 'save') {
118231               canSave = false;
118232
118233               // Attempt to prevent user from creating duplicate changes - see #5200
118234               if (services.osm && services.osm.isChangesetInflight()) {
118235                 _history.clearSaved();
118236                 return;
118237               }
118238
118239             } else {
118240               canSave = context.selectedIDs().every(function (id) {
118241                 var entity = context.hasEntity(id);
118242                 return entity && !entity.isDegenerate();
118243               });
118244             }
118245
118246             if (canSave) {
118247               _history.save();
118248             }
118249             if (_history.hasChanges()) {
118250               return _t('save.unsaved_changes');
118251             }
118252           };
118253
118254           // Debounce save, since it's a synchronous localStorage write,
118255           // and history changes can happen frequently (e.g. when dragging).
118256           context.debouncedSave = debounce(context.save, 350);
118257
118258           function withDebouncedSave(fn) {
118259             return function() {
118260               var result = fn.apply(_history, arguments);
118261               context.debouncedSave();
118262               return result;
118263             };
118264           }
118265
118266
118267           /* Graph */
118268           context.hasEntity = function (id) { return _history.graph().hasEntity(id); };
118269           context.entity = function (id) { return _history.graph().entity(id); };
118270
118271
118272           /* Modes */
118273           var _mode;
118274           context.mode = function () { return _mode; };
118275           context.enter = function (newMode) {
118276             if (_mode) {
118277               _mode.exit();
118278               dispatch$1.call('exit', this$1, _mode);
118279             }
118280
118281             _mode = newMode;
118282             _mode.enter();
118283             dispatch$1.call('enter', this$1, _mode);
118284           };
118285
118286           context.selectedIDs = function () { return (_mode && _mode.selectedIDs && _mode.selectedIDs()) || []; };
118287           context.activeID = function () { return _mode && _mode.activeID && _mode.activeID(); };
118288
118289           var _selectedNoteID;
118290           context.selectedNoteID = function(noteID) {
118291             if (!arguments.length) { return _selectedNoteID; }
118292             _selectedNoteID = noteID;
118293             return context;
118294           };
118295
118296           // NOTE: Don't change the name of this until UI v3 is merged
118297           var _selectedErrorID;
118298           context.selectedErrorID = function(errorID) {
118299             if (!arguments.length) { return _selectedErrorID; }
118300             _selectedErrorID = errorID;
118301             return context;
118302           };
118303
118304
118305           /* Behaviors */
118306           context.install = function (behavior) { return context.surface().call(behavior); };
118307           context.uninstall = function (behavior) { return context.surface().call(behavior.off); };
118308
118309
118310           /* Copy/Paste */
118311           var _copyGraph;
118312           context.copyGraph = function () { return _copyGraph; };
118313
118314           var _copyIDs = [];
118315           context.copyIDs = function(val) {
118316             if (!arguments.length) { return _copyIDs; }
118317             _copyIDs = val;
118318             _copyGraph = _history.graph();
118319             return context;
118320           };
118321
118322           var _copyLonLat;
118323           context.copyLonLat = function(val) {
118324             if (!arguments.length) { return _copyLonLat; }
118325             _copyLonLat = val;
118326             return context;
118327           };
118328
118329
118330           /* Background */
118331           var _background;
118332           context.background = function () { return _background; };
118333
118334
118335           /* Features */
118336           var _features;
118337           context.features = function () { return _features; };
118338           context.hasHiddenConnections = function (id) {
118339             var graph = _history.graph();
118340             var entity = graph.entity(id);
118341             return _features.hasHiddenConnections(entity, graph);
118342           };
118343
118344
118345           /* Photos */
118346           var _photos;
118347           context.photos = function () { return _photos; };
118348
118349
118350           /* Map */
118351           var _map;
118352           context.map = function () { return _map; };
118353           context.layers = function () { return _map.layers(); };
118354           context.surface = function () { return _map.surface; };
118355           context.editableDataEnabled = function () { return _map.editableDataEnabled(); };
118356           context.surfaceRect = function () { return _map.surface.node().getBoundingClientRect(); };
118357           context.editable = function () {
118358             // don't allow editing during save
118359             var mode = context.mode();
118360             if (!mode || mode.id === 'save') { return false; }
118361             return _map.editableDataEnabled();
118362           };
118363
118364
118365           /* Debug */
118366           var _debugFlags = {
118367             tile: false,        // tile boundaries
118368             collision: false,   // label collision bounding boxes
118369             imagery: false,     // imagery bounding polygons
118370             target: false,      // touch targets
118371             downloaded: false   // downloaded data from osm
118372           };
118373           context.debugFlags = function () { return _debugFlags; };
118374           context.getDebug = function (flag) { return flag && _debugFlags[flag]; };
118375           context.setDebug = function(flag, val) {
118376             if (arguments.length === 1) { val = true; }
118377             _debugFlags[flag] = val;
118378             dispatch$1.call('change');
118379             return context;
118380           };
118381
118382
118383           /* Container */
118384           var _container = select(null);
118385           context.container = function(val) {
118386             if (!arguments.length) { return _container; }
118387             _container = val;
118388             _container.classed('ideditor', true);
118389             return context;
118390           };
118391           context.containerNode = function(val) {
118392             if (!arguments.length) { return context.container().node(); }
118393             context.container(select(val));
118394             return context;
118395           };
118396
118397           var _embed;
118398           context.embed = function(val) {
118399             if (!arguments.length) { return _embed; }
118400             _embed = val;
118401             return context;
118402           };
118403
118404
118405           /* Assets */
118406           var _assetPath = '';
118407           context.assetPath = function(val) {
118408             if (!arguments.length) { return _assetPath; }
118409             _assetPath = val;
118410             _mainFileFetcher.assetPath(val);
118411             return context;
118412           };
118413
118414           var _assetMap = {};
118415           context.assetMap = function(val) {
118416             if (!arguments.length) { return _assetMap; }
118417             _assetMap = val;
118418             _mainFileFetcher.assetMap(val);
118419             return context;
118420           };
118421
118422           context.asset = function (val) {
118423             if (/^http(s)?:\/\//i.test(val)) { return val; }
118424             var filename = _assetPath + val;
118425             return _assetMap[filename] || filename;
118426           };
118427
118428           context.imagePath = function (val) { return context.asset(("img/" + val)); };
118429
118430
118431           /* reset (aka flush) */
118432           context.reset = context.flush = function () {
118433             context.debouncedSave.cancel();
118434
118435             Array.from(_deferred).forEach(function (handle) {
118436               window.cancelIdleCallback(handle);
118437               _deferred.delete(handle);
118438             });
118439
118440             Object.values(services).forEach(function (service) {
118441               if (service && typeof service.reset === 'function') {
118442                 service.reset(context);
118443               }
118444             });
118445
118446             context.changeset = null;
118447
118448             _validator.reset();
118449             _features.reset();
118450             _history.reset();
118451             _uploader.reset();
118452
118453             // don't leave stale state in the inspector
118454             context.container().select('.inspector-wrap *').remove();
118455
118456             return context;
118457           };
118458
118459
118460           /* Projections */
118461           context.projection = geoRawMercator();
118462           context.curtainProjection = geoRawMercator();
118463
118464
118465           /* Init */
118466           context.init = function () {
118467
118468             instantiateInternal();
118469
118470             initializeDependents();
118471
118472             return context;
118473
118474             // Load variables and properties. No property of `context` should be accessed
118475             // until this is complete since load statuses are indeterminate. The order
118476             // of instantiation shouldn't matter.
118477             function instantiateInternal() {
118478
118479               _history = coreHistory(context);
118480               context.graph = _history.graph;
118481               context.pauseChangeDispatch = _history.pauseChangeDispatch;
118482               context.resumeChangeDispatch = _history.resumeChangeDispatch;
118483               context.perform = withDebouncedSave(_history.perform);
118484               context.replace = withDebouncedSave(_history.replace);
118485               context.pop = withDebouncedSave(_history.pop);
118486               context.overwrite = withDebouncedSave(_history.overwrite);
118487               context.undo = withDebouncedSave(_history.undo);
118488               context.redo = withDebouncedSave(_history.redo);
118489
118490               _validator = coreValidator(context);
118491               _uploader = coreUploader(context);
118492
118493               _background = rendererBackground(context);
118494               _features = rendererFeatures(context);
118495               _map = rendererMap(context);
118496               _photos = rendererPhotos(context);
118497
118498               _ui = uiInit(context);
118499             }
118500
118501             // Set up objects that might need to access properties of `context`. The order
118502             // might matter if dependents make calls to each other. Be wary of async calls.
118503             function initializeDependents() {
118504
118505               if (context.initialHashParams.presets) {
118506                 _mainPresetIndex.addablePresetIDs(new Set(context.initialHashParams.presets.split(',')));
118507               }
118508
118509               if (context.initialHashParams.locale) {
118510                 _mainLocalizer.preferredLocaleCodes(context.initialHashParams.locale);
118511               }
118512
118513               // kick off some async work
118514               _mainLocalizer.ensureLoaded();
118515               _background.ensureLoaded();
118516               _mainPresetIndex.ensureLoaded();
118517
118518               Object.values(services).forEach(function (service) {
118519                 if (service && typeof service.init === 'function') {
118520                   service.init();
118521                 }
118522               });
118523
118524               _map.init();
118525               _validator.init();
118526               _features.init();
118527               _photos.init();
118528
118529               if (services.maprules && context.initialHashParams.maprules) {
118530                 d3_json(context.initialHashParams.maprules)
118531                   .then(function (mapcss) {
118532                     services.maprules.init();
118533                     mapcss.forEach(function (mapcssSelector) { return services.maprules.addRule(mapcssSelector); });
118534                   })
118535                   .catch(function () { /* ignore */ });
118536               }
118537
118538               // if the container isn't available, e.g. when testing, don't load the UI
118539               if (!context.container().empty()) { _ui.ensureLoaded(); }
118540             }
118541           };
118542
118543
118544           return context;
118545         }
118546
118547         // When `debug = true`, we use `Object.freeze` on immutables in iD.
118548         // This is only done in testing because of the performance penalty.
118549         var debug = false;
118550         var d3 = {
118551           customEvent: customEvent,
118552           dispatch:  dispatch,
118553           event:  event,
118554           geoMercator: mercator,
118555           geoProjection: projection,
118556           polygonArea: d3_polygonArea,
118557           polygonCentroid: d3_polygonCentroid,
118558           select: select,
118559           selectAll: selectAll,
118560           timerFlush: timerFlush
118561         };
118562
118563         var iD = /*#__PURE__*/Object.freeze({
118564                 __proto__: null,
118565                 debug: debug,
118566                 d3: d3,
118567                 actionAddEntity: actionAddEntity,
118568                 actionAddMember: actionAddMember,
118569                 actionAddMidpoint: actionAddMidpoint,
118570                 actionAddVertex: actionAddVertex,
118571                 actionChangeMember: actionChangeMember,
118572                 actionChangePreset: actionChangePreset,
118573                 actionChangeTags: actionChangeTags,
118574                 actionCircularize: actionCircularize,
118575                 actionConnect: actionConnect,
118576                 actionCopyEntities: actionCopyEntities,
118577                 actionDeleteMember: actionDeleteMember,
118578                 actionDeleteMultiple: actionDeleteMultiple,
118579                 actionDeleteNode: actionDeleteNode,
118580                 actionDeleteRelation: actionDeleteRelation,
118581                 actionDeleteWay: actionDeleteWay,
118582                 actionDiscardTags: actionDiscardTags,
118583                 actionDisconnect: actionDisconnect,
118584                 actionExtract: actionExtract,
118585                 actionJoin: actionJoin,
118586                 actionMerge: actionMerge,
118587                 actionMergeNodes: actionMergeNodes,
118588                 actionMergePolygon: actionMergePolygon,
118589                 actionMergeRemoteChanges: actionMergeRemoteChanges,
118590                 actionMove: actionMove,
118591                 actionMoveMember: actionMoveMember,
118592                 actionMoveNode: actionMoveNode,
118593                 actionNoop: actionNoop,
118594                 actionOrthogonalize: actionOrthogonalize,
118595                 actionRestrictTurn: actionRestrictTurn,
118596                 actionReverse: actionReverse,
118597                 actionRevert: actionRevert,
118598                 actionRotate: actionRotate,
118599                 actionSplit: actionSplit,
118600                 actionStraightenNodes: actionStraightenNodes,
118601                 actionStraightenWay: actionStraightenWay,
118602                 actionUnrestrictTurn: actionUnrestrictTurn,
118603                 actionReflect: actionReflect,
118604                 actionUpgradeTags: actionUpgradeTags,
118605                 behaviorAddWay: behaviorAddWay,
118606                 behaviorBreathe: behaviorBreathe,
118607                 behaviorDrag: behaviorDrag,
118608                 behaviorDrawWay: behaviorDrawWay,
118609                 behaviorDraw: behaviorDraw,
118610                 behaviorEdit: behaviorEdit,
118611                 behaviorHash: behaviorHash,
118612                 behaviorHover: behaviorHover,
118613                 behaviorLasso: behaviorLasso,
118614                 behaviorOperation: behaviorOperation,
118615                 behaviorPaste: behaviorPaste,
118616                 behaviorSelect: behaviorSelect,
118617                 coreContext: coreContext,
118618                 coreFileFetcher: coreFileFetcher,
118619                 fileFetcher: _mainFileFetcher,
118620                 coreDifference: coreDifference,
118621                 coreGraph: coreGraph,
118622                 coreHistory: coreHistory,
118623                 coreLocalizer: coreLocalizer,
118624                 t: _t,
118625                 localizer: _mainLocalizer,
118626                 prefs: corePreferences,
118627                 coreTree: coreTree,
118628                 coreUploader: coreUploader,
118629                 coreValidator: coreValidator,
118630                 geoExtent: geoExtent,
118631                 geoLatToMeters: geoLatToMeters,
118632                 geoLonToMeters: geoLonToMeters,
118633                 geoMetersToLat: geoMetersToLat,
118634                 geoMetersToLon: geoMetersToLon,
118635                 geoMetersToOffset: geoMetersToOffset,
118636                 geoOffsetToMeters: geoOffsetToMeters,
118637                 geoScaleToZoom: geoScaleToZoom,
118638                 geoSphericalClosestNode: geoSphericalClosestNode,
118639                 geoSphericalDistance: geoSphericalDistance,
118640                 geoZoomToScale: geoZoomToScale,
118641                 geoAngle: geoAngle,
118642                 geoChooseEdge: geoChooseEdge,
118643                 geoEdgeEqual: geoEdgeEqual,
118644                 geoGetSmallestSurroundingRectangle: geoGetSmallestSurroundingRectangle,
118645                 geoHasLineIntersections: geoHasLineIntersections,
118646                 geoHasSelfIntersections: geoHasSelfIntersections,
118647                 geoRotate: geoRotate,
118648                 geoLineIntersection: geoLineIntersection,
118649                 geoPathHasIntersections: geoPathHasIntersections,
118650                 geoPathIntersections: geoPathIntersections,
118651                 geoPathLength: geoPathLength,
118652                 geoPointInPolygon: geoPointInPolygon,
118653                 geoPolygonContainsPolygon: geoPolygonContainsPolygon,
118654                 geoPolygonIntersectsPolygon: geoPolygonIntersectsPolygon,
118655                 geoViewportEdge: geoViewportEdge,
118656                 geoRawMercator: geoRawMercator,
118657                 geoVecAdd: geoVecAdd,
118658                 geoVecAngle: geoVecAngle,
118659                 geoVecCross: geoVecCross,
118660                 geoVecDot: geoVecDot,
118661                 geoVecEqual: geoVecEqual,
118662                 geoVecFloor: geoVecFloor,
118663                 geoVecInterp: geoVecInterp,
118664                 geoVecLength: geoVecLength,
118665                 geoVecLengthSquare: geoVecLengthSquare,
118666                 geoVecNormalize: geoVecNormalize,
118667                 geoVecNormalizedDot: geoVecNormalizedDot,
118668                 geoVecProject: geoVecProject,
118669                 geoVecSubtract: geoVecSubtract,
118670                 geoVecScale: geoVecScale,
118671                 geoOrthoNormalizedDotProduct: geoOrthoNormalizedDotProduct,
118672                 geoOrthoCalcScore: geoOrthoCalcScore,
118673                 geoOrthoMaxOffsetAngle: geoOrthoMaxOffsetAngle,
118674                 geoOrthoCanOrthogonalize: geoOrthoCanOrthogonalize,
118675                 modeAddArea: modeAddArea,
118676                 modeAddLine: modeAddLine,
118677                 modeAddPoint: modeAddPoint,
118678                 modeAddNote: modeAddNote,
118679                 modeBrowse: modeBrowse,
118680                 modeDragNode: modeDragNode,
118681                 modeDragNote: modeDragNote,
118682                 modeDrawArea: modeDrawArea,
118683                 modeDrawLine: modeDrawLine,
118684                 modeMove: modeMove,
118685                 modeRotate: modeRotate,
118686                 modeSave: modeSave,
118687                 modeSelect: modeSelect,
118688                 modeSelectData: modeSelectData,
118689                 modeSelectError: modeSelectError,
118690                 modeSelectNote: modeSelectNote,
118691                 operationCircularize: operationCircularize,
118692                 operationContinue: operationContinue,
118693                 operationCopy: operationCopy,
118694                 operationDelete: operationDelete,
118695                 operationDisconnect: operationDisconnect,
118696                 operationDowngrade: operationDowngrade,
118697                 operationExtract: operationExtract,
118698                 operationMerge: operationMerge,
118699                 operationMove: operationMove,
118700                 operationOrthogonalize: operationOrthogonalize,
118701                 operationPaste: operationPaste,
118702                 operationReflectShort: operationReflectShort,
118703                 operationReflectLong: operationReflectLong,
118704                 operationReverse: operationReverse,
118705                 operationRotate: operationRotate,
118706                 operationSplit: operationSplit,
118707                 operationStraighten: operationStraighten,
118708                 osmChangeset: osmChangeset,
118709                 osmEntity: osmEntity,
118710                 osmNode: osmNode,
118711                 osmNote: osmNote,
118712                 osmRelation: osmRelation,
118713                 osmWay: osmWay,
118714                 QAItem: QAItem,
118715                 osmIntersection: osmIntersection,
118716                 osmTurn: osmTurn,
118717                 osmInferRestriction: osmInferRestriction,
118718                 osmLanes: osmLanes,
118719                 osmOldMultipolygonOuterMemberOfRelation: osmOldMultipolygonOuterMemberOfRelation,
118720                 osmIsOldMultipolygonOuterMember: osmIsOldMultipolygonOuterMember,
118721                 osmOldMultipolygonOuterMember: osmOldMultipolygonOuterMember,
118722                 osmJoinWays: osmJoinWays,
118723                 get osmAreaKeys () { return osmAreaKeys; },
118724                 osmSetAreaKeys: osmSetAreaKeys,
118725                 osmTagSuggestingArea: osmTagSuggestingArea,
118726                 get osmPointTags () { return osmPointTags; },
118727                 osmSetPointTags: osmSetPointTags,
118728                 get osmVertexTags () { return osmVertexTags; },
118729                 osmSetVertexTags: osmSetVertexTags,
118730                 osmNodeGeometriesForTags: osmNodeGeometriesForTags,
118731                 osmOneWayTags: osmOneWayTags,
118732                 osmPavedTags: osmPavedTags,
118733                 osmIsInterestingTag: osmIsInterestingTag,
118734                 osmRoutableHighwayTagValues: osmRoutableHighwayTagValues,
118735                 osmFlowingWaterwayTagValues: osmFlowingWaterwayTagValues,
118736                 osmRailwayTrackTagValues: osmRailwayTrackTagValues,
118737                 presetCategory: presetCategory,
118738                 presetCollection: presetCollection,
118739                 presetField: presetField,
118740                 presetPreset: presetPreset,
118741                 presetManager: _mainPresetIndex,
118742                 presetIndex: presetIndex,
118743                 rendererBackgroundSource: rendererBackgroundSource,
118744                 rendererBackground: rendererBackground,
118745                 rendererFeatures: rendererFeatures,
118746                 rendererMap: rendererMap,
118747                 rendererPhotos: rendererPhotos,
118748                 rendererTileLayer: rendererTileLayer,
118749                 services: services,
118750                 serviceKeepRight: serviceKeepRight,
118751                 serviceImproveOSM: serviceImproveOSM,
118752                 serviceOsmose: serviceOsmose,
118753                 serviceMapillary: serviceMapillary,
118754                 serviceMapRules: serviceMapRules,
118755                 serviceNominatim: serviceNominatim,
118756                 serviceOpenstreetcam: serviceOpenstreetcam,
118757                 serviceOsm: serviceOsm,
118758                 serviceOsmWikibase: serviceOsmWikibase,
118759                 serviceStreetside: serviceStreetside,
118760                 serviceTaginfo: serviceTaginfo,
118761                 serviceVectorTile: serviceVectorTile,
118762                 serviceWikidata: serviceWikidata,
118763                 serviceWikipedia: serviceWikipedia,
118764                 svgAreas: svgAreas,
118765                 svgData: svgData,
118766                 svgDebug: svgDebug,
118767                 svgDefs: svgDefs,
118768                 svgKeepRight: svgKeepRight,
118769                 svgIcon: svgIcon,
118770                 svgGeolocate: svgGeolocate,
118771                 svgLabels: svgLabels,
118772                 svgLayers: svgLayers,
118773                 svgLines: svgLines,
118774                 svgMapillaryImages: svgMapillaryImages,
118775                 svgMapillarySigns: svgMapillarySigns,
118776                 svgMidpoints: svgMidpoints,
118777                 svgNotes: svgNotes,
118778                 svgMarkerSegments: svgMarkerSegments,
118779                 svgOpenstreetcamImages: svgOpenstreetcamImages,
118780                 svgOsm: svgOsm,
118781                 svgPassiveVertex: svgPassiveVertex,
118782                 svgPath: svgPath,
118783                 svgPointTransform: svgPointTransform,
118784                 svgPoints: svgPoints,
118785                 svgRelationMemberTags: svgRelationMemberTags,
118786                 svgSegmentWay: svgSegmentWay,
118787                 svgStreetside: svgStreetside,
118788                 svgTagClasses: svgTagClasses,
118789                 svgTagPattern: svgTagPattern,
118790                 svgTouch: svgTouch,
118791                 svgTurns: svgTurns,
118792                 svgVertices: svgVertices,
118793                 uiFieldDefaultCheck: uiFieldCheck,
118794                 uiFieldOnewayCheck: uiFieldCheck,
118795                 uiFieldCheck: uiFieldCheck,
118796                 uiFieldMultiCombo: uiFieldCombo,
118797                 uiFieldNetworkCombo: uiFieldCombo,
118798                 uiFieldSemiCombo: uiFieldCombo,
118799                 uiFieldTypeCombo: uiFieldCombo,
118800                 uiFieldCombo: uiFieldCombo,
118801                 uiFieldUrl: uiFieldText,
118802                 uiFieldIdentifier: uiFieldText,
118803                 uiFieldNumber: uiFieldText,
118804                 uiFieldTel: uiFieldText,
118805                 uiFieldEmail: uiFieldText,
118806                 uiFieldText: uiFieldText,
118807                 uiFieldAccess: uiFieldAccess,
118808                 uiFieldAddress: uiFieldAddress,
118809                 uiFieldCycleway: uiFieldCycleway,
118810                 uiFieldLanes: uiFieldLanes,
118811                 uiFieldLocalized: uiFieldLocalized,
118812                 uiFieldMaxspeed: uiFieldMaxspeed,
118813                 uiFieldStructureRadio: uiFieldRadio,
118814                 uiFieldRadio: uiFieldRadio,
118815                 uiFieldRestrictions: uiFieldRestrictions,
118816                 uiFieldTextarea: uiFieldTextarea,
118817                 uiFieldWikidata: uiFieldWikidata,
118818                 uiFieldWikipedia: uiFieldWikipedia,
118819                 uiFields: uiFields,
118820                 uiIntro: uiIntro,
118821                 uiPanelBackground: uiPanelBackground,
118822                 uiPanelHistory: uiPanelHistory,
118823                 uiPanelLocation: uiPanelLocation,
118824                 uiPanelMeasurement: uiPanelMeasurement,
118825                 uiInfoPanels: uiInfoPanels,
118826                 uiPaneBackground: uiPaneBackground,
118827                 uiPaneHelp: uiPaneHelp,
118828                 uiPaneIssues: uiPaneIssues,
118829                 uiPaneMapData: uiPaneMapData,
118830                 uiPanePreferences: uiPanePreferences,
118831                 uiSectionBackgroundDisplayOptions: uiSectionBackgroundDisplayOptions,
118832                 uiSectionBackgroundList: uiSectionBackgroundList,
118833                 uiSectionBackgroundOffset: uiSectionBackgroundOffset,
118834                 uiSectionChanges: uiSectionChanges,
118835                 uiSectionDataLayers: uiSectionDataLayers,
118836                 uiSectionEntityIssues: uiSectionEntityIssues,
118837                 uiSectionFeatureType: uiSectionFeatureType,
118838                 uiSectionMapFeatures: uiSectionMapFeatures,
118839                 uiSectionMapStyleOptions: uiSectionMapStyleOptions,
118840                 uiSectionOverlayList: uiSectionOverlayList,
118841                 uiSectionPhotoOverlays: uiSectionPhotoOverlays,
118842                 uiSectionPresetFields: uiSectionPresetFields,
118843                 uiSectionPrivacy: uiSectionPrivacy,
118844                 uiSectionRawMemberEditor: uiSectionRawMemberEditor,
118845                 uiSectionRawMembershipEditor: uiSectionRawMembershipEditor,
118846                 uiSectionRawTagEditor: uiSectionRawTagEditor,
118847                 uiSectionSelectionList: uiSectionSelectionList,
118848                 uiSectionValidationIssues: uiSectionValidationIssues,
118849                 uiSectionValidationOptions: uiSectionValidationOptions,
118850                 uiSectionValidationRules: uiSectionValidationRules,
118851                 uiSectionValidationStatus: uiSectionValidationStatus,
118852                 uiSettingsCustomBackground: uiSettingsCustomBackground,
118853                 uiSettingsCustomData: uiSettingsCustomData,
118854                 uiInit: uiInit,
118855                 uiAccount: uiAccount,
118856                 uiAttribution: uiAttribution,
118857                 uiChangesetEditor: uiChangesetEditor,
118858                 uiCmd: uiCmd,
118859                 uiCombobox: uiCombobox,
118860                 uiCommit: uiCommit,
118861                 uiCommitWarnings: uiCommitWarnings,
118862                 uiConfirm: uiConfirm,
118863                 uiConflicts: uiConflicts,
118864                 uiContributors: uiContributors,
118865                 uiCurtain: uiCurtain,
118866                 uiDataEditor: uiDataEditor,
118867                 uiDataHeader: uiDataHeader,
118868                 uiDisclosure: uiDisclosure,
118869                 uiEditMenu: uiEditMenu,
118870                 uiEntityEditor: uiEntityEditor,
118871                 uiFeatureInfo: uiFeatureInfo,
118872                 uiFeatureList: uiFeatureList,
118873                 uiField: uiField,
118874                 uiFieldHelp: uiFieldHelp,
118875                 uiFlash: uiFlash,
118876                 uiFormFields: uiFormFields,
118877                 uiFullScreen: uiFullScreen,
118878                 uiGeolocate: uiGeolocate,
118879                 uiImproveOsmComments: uiImproveOsmComments,
118880                 uiImproveOsmDetails: uiImproveOsmDetails,
118881                 uiImproveOsmEditor: uiImproveOsmEditor,
118882                 uiImproveOsmHeader: uiImproveOsmHeader,
118883                 uiInfo: uiInfo,
118884                 uiInspector: uiInspector,
118885                 uiIssuesInfo: uiIssuesInfo,
118886                 uiKeepRightDetails: uiKeepRightDetails,
118887                 uiKeepRightEditor: uiKeepRightEditor,
118888                 uiKeepRightHeader: uiKeepRightHeader,
118889                 uiLasso: uiLasso,
118890                 uiLoading: uiLoading,
118891                 uiMapInMap: uiMapInMap,
118892                 uiModal: uiModal,
118893                 uiNotice: uiNotice,
118894                 uiNoteComments: uiNoteComments,
118895                 uiNoteEditor: uiNoteEditor,
118896                 uiNoteHeader: uiNoteHeader,
118897                 uiNoteReport: uiNoteReport,
118898                 uiPopover: uiPopover,
118899                 uiPresetIcon: uiPresetIcon,
118900                 uiPresetList: uiPresetList,
118901                 uiRestore: uiRestore,
118902                 uiScale: uiScale,
118903                 uiSidebar: uiSidebar,
118904                 uiSourceSwitch: uiSourceSwitch,
118905                 uiSpinner: uiSpinner,
118906                 uiSplash: uiSplash,
118907                 uiStatus: uiStatus,
118908                 uiSuccess: uiSuccess,
118909                 uiTagReference: uiTagReference,
118910                 uiToggle: uiToggle,
118911                 uiTooltip: uiTooltip,
118912                 uiVersion: uiVersion,
118913                 uiViewOnOSM: uiViewOnOSM,
118914                 uiViewOnKeepRight: uiViewOnKeepRight,
118915                 uiZoom: uiZoom,
118916                 utilAesEncrypt: utilAesEncrypt,
118917                 utilAesDecrypt: utilAesDecrypt,
118918                 utilArrayChunk: utilArrayChunk,
118919                 utilArrayDifference: utilArrayDifference,
118920                 utilArrayFlatten: utilArrayFlatten,
118921                 utilArrayGroupBy: utilArrayGroupBy,
118922                 utilArrayIdentical: utilArrayIdentical,
118923                 utilArrayIntersection: utilArrayIntersection,
118924                 utilArrayUnion: utilArrayUnion,
118925                 utilArrayUniq: utilArrayUniq,
118926                 utilArrayUniqBy: utilArrayUniqBy,
118927                 utilAsyncMap: utilAsyncMap,
118928                 utilCleanTags: utilCleanTags,
118929                 utilCombinedTags: utilCombinedTags,
118930                 utilDeepMemberSelector: utilDeepMemberSelector,
118931                 utilDetect: utilDetect,
118932                 utilDisplayName: utilDisplayName,
118933                 utilDisplayNameForPath: utilDisplayNameForPath,
118934                 utilDisplayType: utilDisplayType,
118935                 utilDisplayLabel: utilDisplayLabel,
118936                 utilEntityRoot: utilEntityRoot,
118937                 utilEditDistance: utilEditDistance,
118938                 utilEntitySelector: utilEntitySelector,
118939                 utilEntityOrMemberSelector: utilEntityOrMemberSelector,
118940                 utilEntityOrDeepMemberSelector: utilEntityOrDeepMemberSelector,
118941                 utilFastMouse: utilFastMouse,
118942                 utilFunctor: utilFunctor,
118943                 utilGetAllNodes: utilGetAllNodes,
118944                 utilGetSetValue: utilGetSetValue,
118945                 utilHashcode: utilHashcode,
118946                 utilHighlightEntities: utilHighlightEntities,
118947                 utilKeybinding: utilKeybinding,
118948                 utilNoAuto: utilNoAuto,
118949                 utilObjectOmit: utilObjectOmit,
118950                 utilPrefixCSSProperty: utilPrefixCSSProperty,
118951                 utilPrefixDOMProperty: utilPrefixDOMProperty,
118952                 utilQsString: utilQsString,
118953                 utilRebind: utilRebind,
118954                 utilSafeClassName: utilSafeClassName,
118955                 utilSetTransform: utilSetTransform,
118956                 utilSessionMutex: utilSessionMutex,
118957                 utilStringQs: utilStringQs,
118958                 utilTagDiff: utilTagDiff,
118959                 utilTagText: utilTagText,
118960                 utilTiler: utilTiler,
118961                 utilTotalExtent: utilTotalExtent,
118962                 utilTriggerEvent: utilTriggerEvent,
118963                 utilUnicodeCharsCount: utilUnicodeCharsCount,
118964                 utilUnicodeCharsTruncated: utilUnicodeCharsTruncated,
118965                 utilUniqueDomId: utilUniqueDomId,
118966                 utilWrap: utilWrap,
118967                 validationAlmostJunction: validationAlmostJunction,
118968                 validationCloseNodes: validationCloseNodes,
118969                 validationCrossingWays: validationCrossingWays,
118970                 validationDisconnectedWay: validationDisconnectedWay,
118971                 validationFormatting: validationFormatting,
118972                 validationHelpRequest: validationHelpRequest,
118973                 validationImpossibleOneway: validationImpossibleOneway,
118974                 validationIncompatibleSource: validationIncompatibleSource,
118975                 validationMaprules: validationMaprules,
118976                 validationMismatchedGeometry: validationMismatchedGeometry,
118977                 validationMissingRole: validationMissingRole,
118978                 validationMissingTag: validationMissingTag,
118979                 validationOutdatedTags: validationOutdatedTags,
118980                 validationPrivateData: validationPrivateData,
118981                 validationSuspiciousName: validationSuspiciousName,
118982                 validationUnsquareWay: validationUnsquareWay
118983         });
118984
118985         // polyfill requestIdleCallback
118986         window.requestIdleCallback = window.requestIdleCallback ||
118987             function(cb) {
118988                 var start = Date.now();
118989                 return window.requestAnimationFrame(function() {
118990                     cb({
118991                         didTimeout: false,
118992                         timeRemaining: function() {
118993                             return Math.max(0, 50 - (Date.now() - start));
118994                         }
118995                     });
118996                 });
118997             };
118998
118999         window.cancelIdleCallback = window.cancelIdleCallback ||
119000             function(id) {
119001                 window.cancelAnimationFrame(id);
119002             };
119003         window.iD = iD;
119004
119005 }());